Unverified Commit fa715085 authored by BaiJiangJie's avatar BaiJiangJie Committed by GitHub

Merge pull request #3243 from jumpserver/gather_asset_user

[Feature] 收集资产用户
parents 737f0bc5 6fc666e6
...@@ -6,3 +6,4 @@ from .node import * ...@@ -6,3 +6,4 @@ from .node import *
from .domain import * from .domain import *
from .cmd_filter import * from .cmd_filter import *
from .asset_user import * from .asset_user import *
from .gathered_user import *
...@@ -19,7 +19,7 @@ from rest_framework import generics ...@@ -19,7 +19,7 @@ from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from common.mixins import IDInCacheFilterMixin from common.mixins import CommonApiMixin
from common.utils import get_logger from common.utils import get_logger
from ..hands import IsOrgAdmin from ..hands import IsOrgAdmin
from ..models import AdminUser, Asset from ..models import AdminUser, Asset
......
...@@ -5,9 +5,7 @@ import random ...@@ -5,9 +5,7 @@ import random
from rest_framework import generics from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.db.models import Q
from common.utils import get_logger, get_object_or_none from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
...@@ -16,7 +14,7 @@ from ..models import Asset, AdminUser, Node ...@@ -16,7 +14,7 @@ from ..models import Asset, AdminUser, Node
from .. import serializers from .. import serializers
from ..tasks import update_asset_hardware_info_manual, \ from ..tasks import update_asset_hardware_info_manual, \
test_asset_connectivity_manual test_asset_connectivity_manual
from ..utils import LabelFilter from ..filters import AssetByNodeFilterBackend, LabelFilterBackend
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -27,7 +25,7 @@ __all__ = [ ...@@ -27,7 +25,7 @@ __all__ = [
] ]
class AssetViewSet(LabelFilter, OrgBulkModelViewSet): class AssetViewSet(OrgBulkModelViewSet):
""" """
API endpoint that allows Asset to be viewed or edited. API endpoint that allows Asset to be viewed or edited.
""" """
...@@ -37,7 +35,8 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet): ...@@ -37,7 +35,8 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet):
queryset = Asset.objects.all() queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer serializer_class = serializers.AssetSerializer
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
success_message = _("%(hostname)s was %(action)s successfully") extra_filter_backends = [AssetByNodeFilterBackend, LabelFilterBackend]
custom_filter_fields = ['admin_user_id']
def set_assets_node(self, assets): def set_assets_node(self, assets):
if not isinstance(assets, list): if not isinstance(assets, list):
...@@ -54,30 +53,6 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet): ...@@ -54,30 +53,6 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet):
assets = serializer.save() assets = serializer.save()
self.set_assets_node(assets) self.set_assets_node(assets)
def filter_node(self, queryset):
node_id = self.request.query_params.get("node_id")
if not node_id:
return queryset
node = get_object_or_404(Node, id=node_id)
show_current_asset = self.request.query_params.get("show_current_asset") in ('1', 'true')
# 当前节点是顶层节点, 并且仅显示直接资产
if node.is_org_root() and show_current_asset:
queryset = queryset.filter(
Q(nodes=node_id) | Q(nodes__isnull=True)
).distinct()
# 当前节点是顶层节点,显示所有资产
elif node.is_org_root() and not show_current_asset:
return queryset
# 当前节点不是鼎城节点,只显示直接资产
elif not node.is_org_root() and show_current_asset:
queryset = queryset.filter(nodes=node)
else:
children = node.get_all_children(with_self=True)
queryset = queryset.filter(nodes__in=children).distinct()
return queryset
def filter_admin_user_id(self, queryset): def filter_admin_user_id(self, queryset):
admin_user_id = self.request.query_params.get('admin_user_id') admin_user_id = self.request.query_params.get('admin_user_id')
if not admin_user_id: if not admin_user_id:
...@@ -88,7 +63,6 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet): ...@@ -88,7 +63,6 @@ class AssetViewSet(LabelFilter, OrgBulkModelViewSet):
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset) queryset = super().filter_queryset(queryset)
queryset = self.filter_node(queryset)
queryset = self.filter_admin_user_id(queryset) queryset = self.filter_admin_user_id(queryset)
return queryset return queryset
......
...@@ -10,7 +10,7 @@ from django.http import Http404 ...@@ -10,7 +10,7 @@ from django.http import Http404
from common.permissions import IsOrgAdminOrAppUser, NeedMFAVerify from common.permissions import IsOrgAdminOrAppUser, NeedMFAVerify
from common.utils import get_object_or_none, get_logger from common.utils import get_object_or_none, get_logger
from common.mixins import IDInCacheFilterMixin from common.mixins import CommonApiMixin
from ..backends import AssetUserManager from ..backends import AssetUserManager
from ..models import Asset, Node, SystemUser, AdminUser from ..models import Asset, Node, SystemUser, AdminUser
from .. import serializers from .. import serializers
...@@ -52,7 +52,7 @@ class AssetUserSearchBackend(filters.BaseFilterBackend): ...@@ -52,7 +52,7 @@ class AssetUserSearchBackend(filters.BaseFilterBackend):
return _queryset return _queryset
class AssetUserViewSet(IDInCacheFilterMixin, BulkModelViewSet): class AssetUserViewSet(CommonApiMixin, BulkModelViewSet):
serializer_class = serializers.AssetUserSerializer serializer_class = serializers.AssetUserSerializer
permission_classes = [IsOrgAdminOrAppUser] permission_classes = [IsOrgAdminOrAppUser]
http_method_names = ['get', 'post'] http_method_names = ['get', 'post']
......
# -*- coding: utf-8 -*-
#
from orgs.mixins.api import OrgModelViewSet
from assets.models import GatheredUser
from common.permissions import IsOrgAdmin
from ..serializers import GatheredUserSerializer
from ..filters import AssetRelatedByNodeFilterBackend
__all__ = ['GatheredUserViewSet']
class GatheredUserViewSet(OrgModelViewSet):
queryset = GatheredUser.objects.all()
serializer_class = GatheredUserSerializer
permission_classes = [IsOrgAdmin]
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
filter_fields = ['asset', 'username', 'present']
search_fields = ['username', 'asset__ip', 'asset__hostname']
# -*- coding: utf-8 -*-
#
import coreapi
from rest_framework import filters
from django.db.models import Q
from common.utils import dict_get_any, is_uuid, get_object_or_none
from .models import Node, Label
class AssetByNodeFilterBackend(filters.BaseFilterBackend):
fields = ['node', 'all']
def get_schema_fields(self, view):
return [
coreapi.Field(
name=field, location='query', required=False,
type='string', example='', description='', schema=None,
)
for field in self.fields
]
@staticmethod
def is_query_all(request):
query_all_arg = request.query_params.get('all')
show_current_asset_arg = request.query_params.get('show_current_asset')
query_all = query_all_arg == '1'
if show_current_asset_arg is not None:
query_all = show_current_asset_arg != '1'
return query_all
@staticmethod
def get_query_node(request):
node_id = dict_get_any(request.query_params, ['node', 'node_id'])
if not node_id:
return None, False
if is_uuid(node_id):
node = get_object_or_none(Node, id=node_id)
else:
node = get_object_or_none(Node, key=node_id)
return node, True
@staticmethod
def perform_query(pattern, queryset):
return queryset.filter(nodes__key__regex=pattern)
def filter_queryset(self, request, queryset, view):
node, has_query_arg = self.get_query_node(request)
if not has_query_arg:
return queryset
if node is None:
return queryset.none()
query_all = self.is_query_all(request)
if query_all:
pattern = node.get_all_children_pattern(with_self=True)
else:
pattern = node.get_children_key_pattern(with_self=True)
return self.perform_query(pattern, queryset)
class LabelFilterBackend(filters.BaseFilterBackend):
sep = '#'
query_arg = 'label'
def get_schema_fields(self, view):
example = self.sep.join(['os', 'linux'])
return [
coreapi.Field(
name=self.query_arg, location='query', required=False,
type='string', example=example, description=''
)
]
def get_query_labels(self, request):
labels_query = request.query_params.getlist(self.query_arg)
if not labels_query:
return None
q = None
for kv in labels_query:
if self.sep not in kv:
continue
key, value = kv.strip().split(self.sep)[:2]
if not all([key, value]):
continue
if q:
q |= Q(name=key, value=value)
else:
q = Q(name=key, value=value)
if not q:
return []
labels = Label.objects.filter(q, is_active=True)\
.values_list('id', flat=True)
return labels
def filter_queryset(self, request, queryset, view):
labels = self.get_query_labels(request)
if labels is None:
return queryset
if len(labels) == 0:
return queryset.none()
for label in labels:
queryset = queryset.filter(labels=label)
return queryset
class AssetRelatedByNodeFilterBackend(AssetByNodeFilterBackend):
@staticmethod
def perform_query(pattern, queryset):
return queryset.filter(asset__nodes__key__regex=pattern).distinct()
# Generated by Django 2.1.7 on 2019-09-17 12:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0038_auto_20190911_1634'),
]
operations = [
migrations.AddField(
model_name='authbook',
name='is_active',
field=models.BooleanField(default=True, verbose_name='Is active'),
),
]
# Generated by Django 2.1.7 on 2019-09-17 12:56
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0039_authbook_is_active'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='authbook',
name='username',
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(blank=True, db_index=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
]
# Generated by Django 2.1.7 on 2019-09-18 04:10
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0040_auto_20190917_2056'),
]
operations = [
migrations.CreateModel(
name='GatheredUser',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('username', models.CharField(blank=True, db_index=True, max_length=32, verbose_name='Username')),
('present', models.BooleanField(default=True, verbose_name='Present')),
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset', verbose_name='Asset')),
],
options={'ordering': ['asset'], 'verbose_name': 'GatherUser'},
),
]
...@@ -9,3 +9,4 @@ from .cmd_filter import * ...@@ -9,3 +9,4 @@ from .cmd_filter import *
from .authbook import * from .authbook import *
from .utils import * from .utils import *
from .authbook import * from .authbook import *
from .gathered_user import *
...@@ -13,7 +13,7 @@ __all__ = ['AuthBook'] ...@@ -13,7 +13,7 @@ __all__ = ['AuthBook']
class AuthBookQuerySet(models.QuerySet): class AuthBookQuerySet(models.QuerySet):
def latest_version(self): def latest_version(self):
return self.filter(is_latest=True) return self.filter(is_latest=True).filter(is_active=True)
class AuthBookManager(OrgManager): class AuthBookManager(OrgManager):
...@@ -24,6 +24,7 @@ class AuthBook(AssetUser): ...@@ -24,6 +24,7 @@ class AuthBook(AssetUser):
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset')) asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
is_latest = models.BooleanField(default=False, verbose_name=_('Latest version')) is_latest = models.BooleanField(default=False, verbose_name=_('Latest version'))
version = models.IntegerField(default=1, verbose_name=_('Version')) version = models.IntegerField(default=1, verbose_name=_('Version'))
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
objects = AuthBookManager.from_queryset(AuthBookQuerySet)() objects = AuthBookManager.from_queryset(AuthBookQuerySet)()
backend = "db" backend = "db"
...@@ -34,25 +35,25 @@ class AuthBook(AssetUser): ...@@ -34,25 +35,25 @@ class AuthBook(AssetUser):
class Meta: class Meta:
verbose_name = _('AuthBook') verbose_name = _('AuthBook')
def _set_latest(self): def set_to_latest(self):
self._remove_pre_obj_latest() self.remove_pre_latest()
self.is_latest = True self.is_latest = True
self.save() self.save()
def _get_pre_obj(self): def get_pre_latest(self):
pre_obj = self.__class__.objects.filter( pre_obj = self.__class__.objects.filter(
username=self.username, asset=self.asset username=self.username, asset=self.asset
).latest_version().first() ).latest_version().first()
return pre_obj return pre_obj
def _remove_pre_obj_latest(self): def remove_pre_latest(self):
pre_obj = self._get_pre_obj() pre_obj = self.get_pre_latest()
if pre_obj: if pre_obj:
pre_obj.is_latest = False pre_obj.is_latest = False
pre_obj.save() pre_obj.save()
def _set_version(self): def set_version(self):
pre_obj = self._get_pre_obj() pre_obj = self.get_pre_latest()
if pre_obj: if pre_obj:
self.version = pre_obj.version + 1 self.version = pre_obj.version + 1
else: else:
...@@ -60,8 +61,8 @@ class AuthBook(AssetUser): ...@@ -60,8 +61,8 @@ class AuthBook(AssetUser):
self.save() self.save()
def set_version_and_latest(self): def set_version_and_latest(self):
self._set_version() self.set_version()
self._set_latest() self.set_to_latest()
def get_related_assets(self): def get_related_assets(self):
return [self.asset] return [self.asset]
......
...@@ -26,7 +26,7 @@ logger = get_logger(__file__) ...@@ -26,7 +26,7 @@ logger = get_logger(__file__)
class AssetUser(OrgModelMixin): class AssetUser(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name')) name = models.CharField(max_length=128, verbose_name=_('Name'))
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric]) username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric], db_index=True)
password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) password = fields.EncryptCharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key')) private_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH private key'))
public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key')) public_key = fields.EncryptTextField(blank=True, null=True, verbose_name=_('SSH public key'))
......
# -*- coding: utf-8 -*-
#
import uuid
from django.db import models
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.models import OrgModelMixin
__all__ = ['GatheredUser']
class GatheredUser(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
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_created = models.DateTimeField(auto_now_add=True,
verbose_name=_("Date created"))
date_updated = models.DateTimeField(auto_now=True,
verbose_name=_("Date updated"))
@property
def hostname(self):
return self.asset.hostname
@property
def ip(self):
return self.asset.ip
class Meta:
verbose_name = _('GatherUser')
ordering = ['asset']
def __str__(self):
return '{}: {}'.format(self.asset.hostname, self.username)
...@@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext from django.utils.translation import ugettext
from django.core.cache import cache from django.core.cache import cache
from common.utils import get_logger from common.utils import get_logger, timeit
from orgs.mixins.models import OrgModelMixin, OrgManager from orgs.mixins.models import OrgModelMixin, OrgManager
from orgs.utils import set_current_org, get_current_org, tmp_to_org from orgs.utils import set_current_org, get_current_org, tmp_to_org
from orgs.models import Organization from orgs.models import Organization
...@@ -116,16 +116,24 @@ class FamilyMixin: ...@@ -116,16 +116,24 @@ class FamilyMixin:
def all_children(self): def all_children(self):
return self.get_all_children(with_self=False) return self.get_all_children(with_self=False)
def get_children(self, with_self=False): def get_children_key_pattern(self, with_self=False):
pattern = r'^{0}:[0-9]+$'.format(self.key) pattern = r'^{0}:[0-9]+$'.format(self.key)
if with_self: if with_self:
pattern += r'|^{0}$'.format(self.key) pattern += r'|^{0}$'.format(self.key)
return pattern
def get_children(self, with_self=False):
pattern = self.get_children_key_pattern(with_self=with_self)
return Node.objects.filter(key__regex=pattern) return Node.objects.filter(key__regex=pattern)
def get_all_children(self, with_self=False): def get_all_children_pattern(self, with_self=False):
pattern = r'^{0}:'.format(self.key) pattern = r'^{0}:'.format(self.key)
if with_self: if with_self:
pattern += r'|^{0}$'.format(self.key) pattern += r'|^{0}$'.format(self.key)
return pattern
def get_all_children(self, with_self=False):
pattern = self.get_all_children_pattern(with_self=with_self)
children = Node.objects.filter(key__regex=pattern) children = Node.objects.filter(key__regex=pattern)
return children return children
...@@ -290,14 +298,16 @@ class NodeAssetsMixin: ...@@ -290,14 +298,16 @@ class NodeAssetsMixin:
return self.get_all_assets().valid() return self.get_all_assets().valid()
@classmethod @classmethod
def get_nodes_all_assets(cls, nodes_keys): def get_nodes_all_assets(cls, nodes_keys, extra_assets_ids=None):
from .asset import Asset from .asset import Asset
nodes_keys = cls.clean_children_keys(nodes_keys) nodes_keys = cls.clean_children_keys(nodes_keys)
pattern = set() assets_ids = set()
for key in nodes_keys: for key in nodes_keys:
pattern.add(r'^{0}$|^{0}:'.format(key)) node_assets_ids = cls.tree().all_assets(key)
pattern = '|'.join(list(pattern)) assets_ids.update(set(node_assets_ids))
return Asset.objects.filter(nodes__key__regex=pattern) if extra_assets_ids:
assets_ids.update(set(extra_assets_ids))
return Asset.objects.filter(id__in=assets_ids)
class SomeNodesMixin: class SomeNodesMixin:
......
...@@ -9,3 +9,4 @@ from .node import * ...@@ -9,3 +9,4 @@ from .node import *
from .domain import * from .domain import *
from .cmd_filter import * from .cmd_filter import *
from .asset_user import * from .asset_user import *
from .gathered_user import *
...@@ -53,6 +53,7 @@ class AssetUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): ...@@ -53,6 +53,7 @@ class AssetUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
if not validated_data.get("name") and validated_data.get("username"): if not validated_data.get("name") and validated_data.get("username"):
validated_data["name"] = validated_data["username"] validated_data["name"] = validated_data["username"]
instance = AssetUserManager.create(**validated_data) instance = AssetUserManager.create(**validated_data)
instance.set_version_and_latest()
return instance return instance
......
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from ..models import GatheredUser
class GatheredUserSerializer(OrgResourceModelSerializerMixin):
class Meta:
model = GatheredUser
fields = [
'id', 'asset', 'hostname', 'ip', 'username',
'present', 'date_created', 'date_updated'
]
read_only_fields = fields
labels = {
'hostname': _("Hostname"),
'ip': "IP"
}
...@@ -7,13 +7,14 @@ from django.db.models.signals import ( ...@@ -7,13 +7,14 @@ from django.db.models.signals import (
from django.db.models.aggregates import Count from django.db.models.aggregates import Count
from django.dispatch import receiver from django.dispatch import receiver
from common.utils import get_logger from common.utils import get_logger, timeit
from common.decorator import on_transaction_commit from common.decorator import on_transaction_commit
from .models import Asset, SystemUser, Node, AuthBook from .models import Asset, SystemUser, Node
from .tasks import ( from .tasks import (
update_assets_hardware_info_util, update_assets_hardware_info_util,
test_asset_connectivity_util, test_asset_connectivity_util,
push_system_user_to_assets push_system_user_to_assets,
add_nodes_assets_to_system_users
) )
...@@ -99,7 +100,7 @@ def on_system_user_nodes_change(sender, instance=None, action=None, model=None, ...@@ -99,7 +100,7 @@ def on_system_user_nodes_change(sender, instance=None, action=None, model=None,
""" """
if action != "post_add": if action != "post_add":
return return
logger.info("System user `{}` nodes update signal recv".format(instance)) logger.info("System user nodes update signal recv: {}".format(instance))
queryset = model.objects.filter(pk__in=pk_set) queryset = model.objects.filter(pk__in=pk_set)
if model == Node: if model == Node:
...@@ -108,9 +109,7 @@ def on_system_user_nodes_change(sender, instance=None, action=None, model=None, ...@@ -108,9 +109,7 @@ def on_system_user_nodes_change(sender, instance=None, action=None, model=None,
else: else:
nodes_keys = [instance.key] nodes_keys = [instance.key]
system_users = queryset system_users = queryset
assets = Node.get_nodes_all_assets(nodes_keys).values_list('id', flat=True) add_nodes_assets_to_system_users.delay(nodes_keys, system_users)
for system_user in system_users:
system_user.assets.add(*tuple(assets))
@receiver(m2m_changed, sender=Asset.nodes.through) @receiver(m2m_changed, sender=Asset.nodes.through)
...@@ -190,10 +189,3 @@ def on_asset_nodes_remove(sender, instance=None, action='', model=None, ...@@ -190,10 +189,3 @@ def on_asset_nodes_remove(sender, instance=None, action='', model=None,
def on_node_update_or_created(sender, **kwargs): def on_node_update_or_created(sender, **kwargs):
# 刷新节点 # 刷新节点
Node.refresh_nodes() Node.refresh_nodes()
@receiver(post_save, sender=AuthBook)
def on_auth_book_created(sender, instance=None, created=False, **kwargs):
if created:
logger.debug('Receive create auth book object signal.')
instance.set_version_and_latest()
# ~*~ coding: utf-8 ~*~
import json
import re
import os
from collections import defaultdict
from celery import shared_task
from django.utils.translation import ugettext as _
from django.core.cache import cache
from common.utils import (
capacity_convert, sum_capacity, encrypt_password, get_logger
)
from ops.celery.decorator import (
register_as_period_task, after_app_shutdown_clean_periodic
)
from .models import SystemUser, AdminUser
from .models.utils import Connectivity
from . import const
FORKS = 10
TIMEOUT = 60
logger = get_logger(__file__)
CACHE_MAX_TIME = 60*60*2
disk_pattern = re.compile(r'^hd|sd|xvd|vd|nv')
PERIOD_TASK = os.environ.get("PERIOD_TASK", "on")
def check_asset_can_run_ansible(asset):
if not asset.is_active:
msg = _("Asset has been disabled, skipped: {}").format(asset)
logger.info(msg)
return False
if not asset.is_support_ansible():
msg = _("Asset may not be support ansible, skipped: {}").format(asset)
logger.info(msg)
return False
return True
def clean_hosts(assets):
clean_assets = []
for asset in assets:
if not check_asset_can_run_ansible(asset):
continue
clean_assets.append(asset)
if not clean_assets:
logger.info(_("No assets matched, stop task"))
return clean_assets
def clean_hosts_by_protocol(system_user, assets):
hosts = [
asset for asset in assets
if asset.has_protocol(system_user.protocol)
]
if not hosts:
msg = _("No assets matched related system user protocol, stop task")
logger.info(msg)
return hosts
@shared_task(queue="ansible")
def set_assets_hardware_info(assets, result, **kwargs):
"""
Using ops task run result, to update asset info
@shared_task must be exit, because we using it as a task callback, is must
be a celery task also
:param assets:
:param result:
:param kwargs: {task_name: ""}
:return:
"""
result_raw = result[0]
assets_updated = []
success_result = result_raw.get('ok', {})
for asset in assets:
hostname = asset.hostname
info = success_result.get(hostname, {})
info = info.get('setup', {}).get('ansible_facts', {})
if not info:
logger.error(_("Get asset info failed: {}").format(hostname))
continue
___vendor = info.get('ansible_system_vendor', 'Unknown')
___model = info.get('ansible_product_name', 'Unknown')
___sn = info.get('ansible_product_serial', 'Unknown')
for ___cpu_model in info.get('ansible_processor', []):
if ___cpu_model.endswith('GHz') or ___cpu_model.startswith("Intel"):
break
else:
___cpu_model = 'Unknown'
___cpu_model = ___cpu_model[:48]
___cpu_count = info.get('ansible_processor_count', 0)
___cpu_cores = info.get('ansible_processor_cores', None) or \
len(info.get('ansible_processor', []))
___cpu_vcpus = info.get('ansible_processor_vcpus', 0)
___memory = '%s %s' % capacity_convert(
'{} MB'.format(info.get('ansible_memtotal_mb'))
)
disk_info = {}
for dev, dev_info in info.get('ansible_devices', {}).items():
if disk_pattern.match(dev) and dev_info['removable'] == '0':
disk_info[dev] = dev_info['size']
___disk_total = '%.1f %s' % sum_capacity(disk_info.values())
___disk_info = json.dumps(disk_info)
# ___platform = info.get('ansible_system', 'Unknown')
___os = info.get('ansible_distribution', 'Unknown')
___os_version = info.get('ansible_distribution_version', 'Unknown')
___os_arch = info.get('ansible_architecture', 'Unknown')
___hostname_raw = info.get('ansible_hostname', 'Unknown')
for k, v in locals().items():
if k.startswith('___'):
setattr(asset, k.strip('_'), v)
asset.save()
assets_updated.append(asset)
return assets_updated
@shared_task
def update_assets_hardware_info_util(assets, task_name=None):
"""
Using ansible api to update asset hardware info
:param assets: asset seq
:param task_name: task_name running
:return: result summary ['contacted': {}, 'dark': {}]
"""
from ops.utils import update_or_create_ansible_task
if task_name is None:
task_name = _("Update some assets hardware info")
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
hosts = clean_hosts(assets)
if not hosts:
return {}
created_by = str(assets[0].org_id)
task, created = update_or_create_ansible_task(
task_name, hosts=hosts, tasks=tasks, created_by=created_by,
pattern='all', options=const.TASK_OPTIONS, run_as_admin=True,
)
result = task.run()
set_assets_hardware_info(assets, result)
return result
@shared_task(queue="ansible")
def update_asset_hardware_info_manual(asset):
task_name = _("Update asset hardware info: {}").format(asset.hostname)
update_assets_hardware_info_util(
[asset], task_name=task_name
)
@shared_task(queue="ansible")
def update_assets_hardware_info_period():
"""
Update asset hardware period task
:return:
"""
if PERIOD_TASK != "on":
logger.debug("Period task disabled, update assets hardware info pass")
return
## ADMIN USER CONNECTIVE ##
@shared_task(queue="ansible")
def test_asset_connectivity_util(assets, task_name=None):
from ops.utils import update_or_create_ansible_task
if task_name is None:
task_name = _("Test assets connectivity")
hosts = clean_hosts(assets)
if not hosts:
return {}
hosts_category = {
'linux': {
'hosts': [],
'tasks': const.TEST_ADMIN_USER_CONN_TASKS
},
'windows': {
'hosts': [],
'tasks': const.TEST_WINDOWS_ADMIN_USER_CONN_TASKS
}
}
for host in hosts:
hosts_list = hosts_category['windows']['hosts'] if host.is_windows() \
else hosts_category['linux']['hosts']
hosts_list.append(host)
results_summary = dict(
contacted=defaultdict(dict), dark=defaultdict(dict), success=True
)
created_by = assets[0].org_id
for k, value in hosts_category.items():
if not value['hosts']:
continue
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=value['hosts'], tasks=value['tasks'],
pattern='all', options=const.TASK_OPTIONS, run_as_admin=True,
created_by=created_by,
)
raw, summary = task.run()
success = summary.get('success', False)
contacted = summary.get('contacted', {})
dark = summary.get('dark', {})
results_summary['success'] &= success
results_summary['contacted'].update(contacted)
results_summary['dark'].update(dark)
for asset in assets:
if asset.hostname in results_summary.get('dark', {}).keys():
asset.connectivity = Connectivity.unreachable()
elif asset.hostname in results_summary.get('contacted', {}).keys():
asset.connectivity = Connectivity.reachable()
else:
asset.connectivity = Connectivity.unknown()
return results_summary
@shared_task(queue="ansible")
def test_asset_connectivity_manual(asset):
task_name = _("Test assets connectivity: {}").format(asset)
summary = test_asset_connectivity_util([asset], task_name=task_name)
if summary.get('dark'):
return False, summary['dark']
else:
return True, ""
@shared_task(queue="ansible")
def test_admin_user_connectivity_util(admin_user, task_name):
"""
Test asset admin user can connect or not. Using ansible api do that
:param admin_user:
:param task_name:
:return:
"""
assets = admin_user.get_related_assets()
hosts = clean_hosts(assets)
if not hosts:
return {}
summary = test_asset_connectivity_util(hosts, task_name)
return summary
@shared_task(queue="ansible")
@register_as_period_task(interval=3600)
def test_admin_user_connectivity_period():
"""
A period task that update the ansible task period
"""
if PERIOD_TASK != "on":
logger.debug('Period task off, skip')
return
key = '_JMS_TEST_ADMIN_USER_CONNECTIVITY_PERIOD'
prev_execute_time = cache.get(key)
if prev_execute_time:
logger.debug("Test admin user connectivity, less than 40 minutes, skip")
return
cache.set(key, 1, 60*40)
admin_users = AdminUser.objects.all()
for admin_user in admin_users:
task_name = _("Test admin user connectivity period: {}").format(admin_user.name)
test_admin_user_connectivity_util(admin_user, task_name)
cache.set(key, 1, 60*40)
@shared_task(queue="ansible")
def test_admin_user_connectivity_manual(admin_user):
task_name = _("Test admin user connectivity: {}").format(admin_user.name)
test_admin_user_connectivity_util(admin_user, task_name)
return True
## System user connective ##
@shared_task(queue="ansible")
def test_system_user_connectivity_util(system_user, assets, task_name):
"""
Test system cant connect his assets or not.
:param system_user:
:param assets:
:param task_name:
:return:
"""
from ops.utils import update_or_create_ansible_task
hosts = clean_hosts(assets)
if not hosts:
return {}
hosts = clean_hosts_by_protocol(system_user, hosts)
if not hosts:
return {}
hosts_category = {
'linux': {
'hosts': [],
'tasks': const.TEST_SYSTEM_USER_CONN_TASKS
},
'windows': {
'hosts': [],
'tasks': const.TEST_WINDOWS_SYSTEM_USER_CONN_TASKS
}
}
for host in hosts:
hosts_list = hosts_category['windows']['hosts'] if host.is_windows() \
else hosts_category['linux']['hosts']
hosts_list.append(host)
results_summary = dict(
contacted=defaultdict(dict), dark=defaultdict(dict), success=True
)
for k, value in hosts_category.items():
if not value['hosts']:
continue
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=value['hosts'], tasks=value['tasks'],
pattern='all', options=const.TASK_OPTIONS,
run_as=system_user.username, created_by=system_user.org_id,
)
raw, summary = task.run()
success = summary.get('success', False)
contacted = summary.get('contacted', {})
dark = summary.get('dark', {})
results_summary['success'] &= success
results_summary['contacted'].update(contacted)
results_summary['dark'].update(dark)
system_user.set_connectivity(results_summary)
return results_summary
@shared_task(queue="ansible")
def test_system_user_connectivity_manual(system_user):
task_name = _("Test system user connectivity: {}").format(system_user)
assets = system_user.get_all_assets()
return test_system_user_connectivity_util(system_user, assets, task_name)
@shared_task(queue="ansible")
def test_system_user_connectivity_a_asset(system_user, asset):
task_name = _("Test system user connectivity: {} => {}").format(
system_user, asset
)
return test_system_user_connectivity_util(system_user, [asset], task_name)
@shared_task(queue="ansible")
def test_system_user_connectivity_period():
if PERIOD_TASK != "on":
logger.debug("Period task disabled, test system user connectivity pass")
return
system_users = SystemUser.objects.all()
for system_user in system_users:
task_name = _("Test system user connectivity period: {}").format(system_user)
assets = system_user.get_all_assets()
test_system_user_connectivity_util(system_user, assets, task_name)
#### Push system user tasks ####
def get_push_linux_system_user_tasks(system_user):
tasks = [
{
'name': 'Add user {}'.format(system_user.username),
'action': {
'module': 'user',
'args': 'name={} shell={} state=present'.format(
system_user.username, system_user.shell,
),
}
},
{
'name': 'Add group {}'.format(system_user.username),
'action': {
'module': 'group',
'args': 'name={} state=present'.format(
system_user.username,
),
}
},
{
'name': 'Check home dir exists',
'action': {
'module': 'stat',
'args': 'path=/home/{}'.format(system_user.username)
},
'register': 'home_existed'
},
{
'name': "Set home dir permission",
'action': {
'module': 'file',
'args': "path=/home/{0} owner={0} group={0} mode=700".format(system_user.username)
},
'when': 'home_existed.stat.exists == true'
}
]
if system_user.password:
tasks.append({
'name': 'Set {} password'.format(system_user.username),
'action': {
'module': 'user',
'args': 'name={} shell={} state=present password={}'.format(
system_user.username, system_user.shell,
encrypt_password(system_user.password, salt="K3mIlKK"),
),
}
})
if system_user.public_key:
tasks.append({
'name': 'Set {} authorized key'.format(system_user.username),
'action': {
'module': 'authorized_key',
'args': "user={} state=present key='{}'".format(
system_user.username, system_user.public_key
)
}
})
if system_user.sudo:
sudo = system_user.sudo.replace('\r\n', '\n').replace('\r', '\n')
sudo_list = sudo.split('\n')
sudo_tmp = []
for s in sudo_list:
sudo_tmp.append(s.strip(','))
sudo = ','.join(sudo_tmp)
tasks.append({
'name': 'Set {} sudo setting'.format(system_user.username),
'action': {
'module': 'lineinfile',
'args': "dest=/etc/sudoers state=present regexp='^{0} ALL=' "
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
"validate='visudo -cf %s'".format(
system_user.username, sudo,
)
}
})
return tasks
def get_push_windows_system_user_tasks(system_user):
tasks = []
if system_user.password:
tasks.append({
'name': 'Add user {}'.format(system_user.username),
'action': {
'module': 'win_user',
'args': 'fullname={} '
'name={} '
'password={} '
'state=present '
'update_password=always '
'password_expired=no '
'password_never_expires=yes '
'groups="Users,Remote Desktop Users" '
'groups_action=add '
''.format(system_user.name,
system_user.username,
system_user.password),
}
})
return tasks
def get_push_system_user_tasks(host, system_user):
if host.is_unixlike():
tasks = get_push_linux_system_user_tasks(system_user)
elif host.is_windows():
tasks = get_push_windows_system_user_tasks(system_user)
else:
msg = _(
"The asset {} system platform {} does not "
"support run Ansible tasks".format(host.hostname, host.platform)
)
logger.info(msg)
tasks = []
return tasks
@shared_task(queue="ansible")
def push_system_user_util(system_user, assets, task_name):
from ops.utils import update_or_create_ansible_task
if not system_user.is_need_push():
msg = _("Push system user task skip, auto push not enable or "
"protocol is not ssh or rdp: {}").format(system_user.name)
logger.info(msg)
return {}
# Set root as system user is dangerous
if system_user.username.lower() in ["root", "administrator"]:
msg = _("For security, do not push user {}".format(system_user.username))
logger.info(msg)
return {}
hosts = clean_hosts(assets)
if not hosts:
return {}
hosts = clean_hosts_by_protocol(system_user, hosts)
if not hosts:
return {}
for host in hosts:
system_user.load_specific_asset_auth(host)
tasks = get_push_system_user_tasks(host, system_user)
if not tasks:
continue
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=[host], tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, run_as_admin=True,
created_by=system_user.org_id,
)
task.run()
@shared_task(queue="ansible")
def push_system_user_to_assets_manual(system_user):
assets = system_user.get_all_assets()
task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name=task_name)
@shared_task(queue="ansible")
def push_system_user_a_asset_manual(system_user, asset):
task_name = _("Push system users to asset: {} => {}").format(
system_user.name, asset
)
return push_system_user_util(system_user, [asset], task_name=task_name)
@shared_task(queue="ansible")
def push_system_user_to_assets(system_user, assets):
task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name)
@shared_task
@after_app_shutdown_clean_periodic
def test_system_user_connectability_period():
pass
@shared_task
@after_app_shutdown_clean_periodic
def test_admin_user_connectability_period():
pass
#### Test Asset user connectivity task ####
def get_test_asset_user_connectivity_tasks(asset):
if asset.is_unixlike():
tasks = const.TEST_ASSET_USER_CONN_TASKS
elif asset.is_windows():
tasks = const.TEST_WINDOWS_ASSET_USER_CONN_TASKS
else:
msg = _(
"The asset {} system platform {} does not "
"support run Ansible tasks".format(asset.hostname, asset.platform)
)
logger.info(msg)
tasks = []
return tasks
@shared_task(queue="ansible")
def test_asset_user_connectivity_util(asset_user, task_name, run_as_admin=False):
"""
:param asset_user: <AuthBook>对象
:param task_name:
:param run_as_admin:
:return:
"""
from ops.utils import update_or_create_ansible_task
if not check_asset_can_run_ansible(asset_user.asset):
return
tasks = get_test_asset_user_connectivity_tasks(asset_user.asset)
if not tasks:
logger.debug("No tasks ")
return
args = (task_name,)
kwargs = {
'hosts': [asset_user.asset], 'tasks': tasks,
'pattern': 'all', 'options': const.TASK_OPTIONS,
'created_by': asset_user.org_id,
}
if run_as_admin:
kwargs["run_as_admin"] = True
else:
kwargs["run_as"] = asset_user.username
task, created = update_or_create_ansible_task(*args, **kwargs)
raw, summary = task.run()
asset_user.set_connectivity(summary)
@shared_task(queue="ansible")
def test_asset_users_connectivity_manual(asset_users, run_as_admin=False):
"""
:param asset_users: <AuthBook>对象
"""
for asset_user in asset_users:
task_name = _("Test asset user connectivity: {}").format(asset_user)
test_asset_user_connectivity_util(asset_user, task_name, run_as_admin=run_as_admin)
# @shared_task
# @register_as_period_task(interval=3600)
# @after_app_ready_start
# @after_app_shutdown_clean_periodic
# def push_system_user_period():
# for system_user in SystemUser.objects.all():
# push_system_user_related_nodes(system_user)
# -*- coding: utf-8 -*-
#
from .utils import *
from .common import *
from .admin_user_connectivity import *
from .asset_connectivity import *
from .asset_user_connectivity import *
from .gather_asset_users import *
from .gather_asset_hardware_info import *
from .push_system_user import *
from .system_user_connectivity import *
# ~*~ coding: utf-8 ~*~
from celery import shared_task
from django.utils.translation import ugettext as _
from django.core.cache import cache
from common.utils import get_logger
from ops.celery.decorator import register_as_period_task
from ..models import AdminUser
from .utils import clean_hosts
from .asset_connectivity import test_asset_connectivity_util
from . import const
logger = get_logger(__file__)
__all__ = [
'test_admin_user_connectivity_util', 'test_admin_user_connectivity_manual',
'test_admin_user_connectivity_period'
]
@shared_task(queue="ansible")
def test_admin_user_connectivity_util(admin_user, task_name):
"""
Test asset admin user can connect or not. Using ansible api do that
:param admin_user:
:param task_name:
:return:
"""
assets = admin_user.get_related_assets()
hosts = clean_hosts(assets)
if not hosts:
return {}
summary = test_asset_connectivity_util(hosts, task_name)
return summary
@shared_task(queue="ansible")
@register_as_period_task(interval=3600)
def test_admin_user_connectivity_period():
"""
A period task that update the ansible task period
"""
if const.PERIOD_TASK_ENABLED:
logger.debug('Period task off, skip')
return
key = '_JMS_TEST_ADMIN_USER_CONNECTIVITY_PERIOD'
prev_execute_time = cache.get(key)
if prev_execute_time:
logger.debug("Test admin user connectivity, less than 40 minutes, skip")
return
cache.set(key, 1, 60*40)
admin_users = AdminUser.objects.all()
for admin_user in admin_users:
task_name = _("Test admin user connectivity period: {}").format(admin_user.name)
test_admin_user_connectivity_util(admin_user, task_name)
cache.set(key, 1, 60*40)
@shared_task(queue="ansible")
def test_admin_user_connectivity_manual(admin_user):
task_name = _("Test admin user connectivity: {}").format(admin_user.name)
test_admin_user_connectivity_util(admin_user, task_name)
return True
# ~*~ coding: utf-8 ~*~
from collections import defaultdict
from celery import shared_task
from django.utils.translation import ugettext as _
from common.utils import get_logger
from ..models.utils import Connectivity
from . import const
from .utils import clean_hosts
logger = get_logger(__file__)
__all__ = ['test_asset_connectivity_util', 'test_asset_connectivity_manual']
@shared_task(queue="ansible")
def test_asset_connectivity_util(assets, task_name=None):
from ops.utils import update_or_create_ansible_task
if task_name is None:
task_name = _("Test assets connectivity")
hosts = clean_hosts(assets)
if not hosts:
return {}
hosts_category = {
'linux': {
'hosts': [],
'tasks': const.TEST_ADMIN_USER_CONN_TASKS
},
'windows': {
'hosts': [],
'tasks': const.TEST_WINDOWS_ADMIN_USER_CONN_TASKS
}
}
for host in hosts:
hosts_list = hosts_category['windows']['hosts'] if host.is_windows() \
else hosts_category['linux']['hosts']
hosts_list.append(host)
results_summary = dict(
contacted=defaultdict(dict), dark=defaultdict(dict), success=True
)
created_by = assets[0].org_id
for k, value in hosts_category.items():
if not value['hosts']:
continue
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=value['hosts'], tasks=value['tasks'],
pattern='all', options=const.TASK_OPTIONS, run_as_admin=True,
created_by=created_by,
)
raw, summary = task.run()
success = summary.get('success', False)
contacted = summary.get('contacted', {})
dark = summary.get('dark', {})
results_summary['success'] &= success
results_summary['contacted'].update(contacted)
results_summary['dark'].update(dark)
for asset in assets:
if asset.hostname in results_summary.get('dark', {}).keys():
asset.connectivity = Connectivity.unreachable()
elif asset.hostname in results_summary.get('contacted', {}).keys():
asset.connectivity = Connectivity.reachable()
else:
asset.connectivity = Connectivity.unknown()
return results_summary
@shared_task(queue="ansible")
def test_asset_connectivity_manual(asset):
task_name = _("Test assets connectivity: {}").format(asset)
summary = test_asset_connectivity_util([asset], task_name=task_name)
if summary.get('dark'):
return False, summary['dark']
else:
return True, ""
# ~*~ coding: utf-8 ~*~
from celery import shared_task
from django.utils.translation import ugettext as _
from common.utils import get_logger
from . import const
from .utils import check_asset_can_run_ansible
logger = get_logger(__file__)
__all__ = [
'test_asset_user_connectivity_util', 'test_asset_users_connectivity_manual',
'get_test_asset_user_connectivity_tasks',
]
def get_test_asset_user_connectivity_tasks(asset):
if asset.is_unixlike():
tasks = const.TEST_ASSET_USER_CONN_TASKS
elif asset.is_windows():
tasks = const.TEST_WINDOWS_ASSET_USER_CONN_TASKS
else:
msg = _(
"The asset {} system platform {} does not "
"support run Ansible tasks".format(asset.hostname, asset.platform)
)
logger.info(msg)
tasks = []
return tasks
@shared_task(queue="ansible")
def test_asset_user_connectivity_util(asset_user, task_name, run_as_admin=False):
"""
:param asset_user: <AuthBook>对象
:param task_name:
:param run_as_admin:
:return:
"""
from ops.utils import update_or_create_ansible_task
if not check_asset_can_run_ansible(asset_user.asset):
return
tasks = get_test_asset_user_connectivity_tasks(asset_user.asset)
if not tasks:
logger.debug("No tasks ")
return
args = (task_name,)
kwargs = {
'hosts': [asset_user.asset], 'tasks': tasks,
'pattern': 'all', 'options': const.TASK_OPTIONS,
'created_by': asset_user.org_id,
}
if run_as_admin:
kwargs["run_as_admin"] = True
else:
kwargs["run_as"] = asset_user.username
task, created = update_or_create_ansible_task(*args, **kwargs)
raw, summary = task.run()
asset_user.set_connectivity(summary)
@shared_task(queue="ansible")
def test_asset_users_connectivity_manual(asset_users, run_as_admin=False):
"""
:param asset_users: <AuthBook>对象
"""
for asset_user in asset_users:
task_name = _("Test asset user connectivity: {}").format(asset_user)
test_asset_user_connectivity_util(asset_user, task_name, run_as_admin=run_as_admin)
# -*- coding: utf-8 -*-
#
from celery import shared_task
__all__ = ['add_nodes_assets_to_system_users']
@shared_task
def add_nodes_assets_to_system_users(nodes_keys, system_users):
from ..models import Node
assets = Node.get_nodes_all_assets(nodes_keys).values_list('id', flat=True)
for system_user in system_users:
system_user.assets.add(*tuple(assets))
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import os
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
PERIOD_TASK_ENABLED = os.environ.get("PERIOD_TASK", "on") == 'on'
UPDATE_ASSETS_HARDWARE_TASKS = [ UPDATE_ASSETS_HARDWARE_TASKS = [
{ {
'name': "setup", 'name': "setup",
...@@ -79,3 +83,22 @@ CONNECTIVITY_CHOICES = ( ...@@ -79,3 +83,22 @@ CONNECTIVITY_CHOICES = (
(CONN_UNKNOWN, _("Unknown")), (CONN_UNKNOWN, _("Unknown")),
) )
GATHER_ASSET_USERS_TASKS = [
{
"name": "gather host users",
"action": {
"module": 'getent',
"args": "database=passwd"
},
},
]
GATHER_ASSET_USERS_TASKS_WINDOWS = [
{
"name": "gather windows host users",
"action": {
"module": 'win_shell',
"args": "net user"
}
}
]
# -*- coding: utf-8 -*-
#
import json
import re
from celery import shared_task
from django.utils.translation import ugettext as _
from common.utils import (
capacity_convert, sum_capacity, get_logger
)
from . import const
from .utils import clean_hosts
logger = get_logger(__file__)
disk_pattern = re.compile(r'^hd|sd|xvd|vd|nv')
__all__ = [
'update_assets_hardware_info_util', 'update_asset_hardware_info_manual',
'update_assets_hardware_info_period',
]
def set_assets_hardware_info(assets, result, **kwargs):
"""
Using ops task run result, to update asset info
@shared_task must be exit, because we using it as a task callback, is must
be a celery task also
:param assets:
:param result:
:param kwargs: {task_name: ""}
:return:
"""
result_raw = result[0]
assets_updated = []
success_result = result_raw.get('ok', {})
for asset in assets:
hostname = asset.hostname
info = success_result.get(hostname, {})
info = info.get('setup', {}).get('ansible_facts', {})
if not info:
logger.error(_("Get asset info failed: {}").format(hostname))
continue
___vendor = info.get('ansible_system_vendor', 'Unknown')
___model = info.get('ansible_product_name', 'Unknown')
___sn = info.get('ansible_product_serial', 'Unknown')
for ___cpu_model in info.get('ansible_processor', []):
if ___cpu_model.endswith('GHz') or ___cpu_model.startswith("Intel"):
break
else:
___cpu_model = 'Unknown'
___cpu_model = ___cpu_model[:48]
___cpu_count = info.get('ansible_processor_count', 0)
___cpu_cores = info.get('ansible_processor_cores', None) or \
len(info.get('ansible_processor', []))
___cpu_vcpus = info.get('ansible_processor_vcpus', 0)
___memory = '%s %s' % capacity_convert(
'{} MB'.format(info.get('ansible_memtotal_mb'))
)
disk_info = {}
for dev, dev_info in info.get('ansible_devices', {}).items():
if disk_pattern.match(dev) and dev_info['removable'] == '0':
disk_info[dev] = dev_info['size']
___disk_total = '%.1f %s' % sum_capacity(disk_info.values())
___disk_info = json.dumps(disk_info)
# ___platform = info.get('ansible_system', 'Unknown')
___os = info.get('ansible_distribution', 'Unknown')
___os_version = info.get('ansible_distribution_version', 'Unknown')
___os_arch = info.get('ansible_architecture', 'Unknown')
___hostname_raw = info.get('ansible_hostname', 'Unknown')
for k, v in locals().items():
if k.startswith('___'):
setattr(asset, k.strip('_'), v)
asset.save()
assets_updated.append(asset)
return assets_updated
@shared_task
def update_assets_hardware_info_util(assets, task_name=None):
"""
Using ansible api to update asset hardware info
:param assets: asset seq
:param task_name: task_name running
:return: result summary ['contacted': {}, 'dark': {}]
"""
from ops.utils import update_or_create_ansible_task
if task_name is None:
task_name = _("Update some assets hardware info")
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
hosts = clean_hosts(assets)
if not hosts:
return {}
created_by = str(assets[0].org_id)
task, created = update_or_create_ansible_task(
task_name, hosts=hosts, tasks=tasks, created_by=created_by,
pattern='all', options=const.TASK_OPTIONS, run_as_admin=True,
)
result = task.run()
set_assets_hardware_info(assets, result)
return True
@shared_task(queue="ansible")
def update_asset_hardware_info_manual(asset):
task_name = _("Update asset hardware info: {}").format(asset.hostname)
update_assets_hardware_info_util(
[asset], task_name=task_name
)
@shared_task(queue="ansible")
def update_assets_hardware_info_period():
"""
Update asset hardware period task
:return:
"""
if not const.PERIOD_TASK_ENABLED:
logger.debug("Period task disabled, update assets hardware info pass")
return
# ~*~ coding: utf-8 ~*~
import re
from collections import defaultdict
from celery import shared_task
from django.utils.translation import ugettext as _
from orgs.utils import tmp_to_org
from common.utils import get_logger
from ..models import GatheredUser, Node
from .utils import clean_hosts
from . import const
__all__ = ['gather_asset_users', 'gather_nodes_asset_users']
logger = get_logger(__name__)
space = re.compile('\s+')
ignore_login_shell = re.compile(r'nologin$|sync$|shutdown$|halt$')
def parse_linux_result_to_users(result):
task_result = {}
for task_name, raw in result.items():
res = raw.get('ansible_facts', {}).get('getent_passwd')
if res:
task_result = res
break
if not task_result or not isinstance(task_result, dict):
return []
users = []
for username, attr in task_result.items():
if ignore_login_shell.search(attr[-1]):
continue
users.append(username)
return users
def parse_windows_result_to_users(result):
task_result = []
for task_name, raw in result.items():
res = raw.get('stdout_lines', {})
if res:
task_result = res
break
if not task_result:
return []
users = []
for i in range(4):
task_result.pop(0)
for i in range(2):
task_result.pop()
for line in task_result:
user = space.split(line)
if user[0]:
users.append(user[0])
return users
def add_asset_users(assets, results):
assets_map = {a.hostname: a for a in assets}
parser_map = {
'linux': parse_linux_result_to_users,
'windows': parse_windows_result_to_users
}
assets_users_map = {}
for platform, platform_results in results.items():
for hostname, res in platform_results.items():
parse = parser_map.get(platform)
users = parse(res)
logger.debug('Gathered host users: {} {}'.format(hostname, users))
asset = assets_map.get(hostname)
if not asset:
continue
assets_users_map[asset] = users
for asset, users in assets_users_map.items():
with tmp_to_org(asset.org_id):
GatheredUser.objects.filter(asset=asset, present=True)\
.update(present=False)
for username in users:
defaults = {'asset': asset, 'username': username, 'present': True}
GatheredUser.objects.update_or_create(
defaults=defaults, asset=asset, username=username,
)
@shared_task(queue="ansible")
def gather_asset_users(assets, task_name=None):
from ops.utils import update_or_create_ansible_task
if task_name is None:
task_name = _("Gather assets users")
assets = clean_hosts(assets)
if not assets:
return
hosts_category = {
'linux': {
'hosts': [],
'tasks': const.GATHER_ASSET_USERS_TASKS
},
'windows': {
'hosts': [],
'tasks': const.GATHER_ASSET_USERS_TASKS_WINDOWS
}
}
for asset in assets:
hosts_list = hosts_category['windows']['hosts'] if asset.is_windows() \
else hosts_category['linux']['hosts']
hosts_list.append(asset)
results = {'linux': defaultdict(dict), 'windows': defaultdict(dict)}
for k, value in hosts_category.items():
if not value['hosts']:
continue
_task_name = '{}: {}'.format(task_name, k)
task, created = update_or_create_ansible_task(
task_name=_task_name, hosts=value['hosts'], tasks=value['tasks'],
pattern='all', options=const.TASK_OPTIONS,
run_as_admin=True, created_by=value['hosts'][0].org_id,
)
raw, summary = task.run()
results[k].update(raw['ok'])
add_asset_users(assets, results)
@shared_task(queue="ansible")
def gather_nodes_asset_users(nodes_key):
assets = Node.get_nodes_all_assets(nodes_key)
assets_groups_by_100 = [assets[i:i+100] for i in range(0, len(assets), 100)]
for _assets in assets_groups_by_100:
gather_asset_users(_assets)
# ~*~ coding: utf-8 ~*~
from celery import shared_task
from django.utils.translation import ugettext as _
from common.utils import encrypt_password, get_logger
from . import const
from .utils import clean_hosts_by_protocol, clean_hosts
logger = get_logger(__file__)
__all__ = [
'push_system_user_util', 'push_system_user_to_assets',
'push_system_user_to_assets_manual', 'push_system_user_a_asset_manual',
]
def get_push_linux_system_user_tasks(system_user):
tasks = [
{
'name': 'Add user {}'.format(system_user.username),
'action': {
'module': 'user',
'args': 'name={} shell={} state=present'.format(
system_user.username, system_user.shell,
),
}
},
{
'name': 'Add group {}'.format(system_user.username),
'action': {
'module': 'group',
'args': 'name={} state=present'.format(
system_user.username,
),
}
},
{
'name': 'Check home dir exists',
'action': {
'module': 'stat',
'args': 'path=/home/{}'.format(system_user.username)
},
'register': 'home_existed'
},
{
'name': "Set home dir permission",
'action': {
'module': 'file',
'args': "path=/home/{0} owner={0} group={0} mode=700".format(system_user.username)
},
'when': 'home_existed.stat.exists == true'
}
]
if system_user.password:
tasks.append({
'name': 'Set {} password'.format(system_user.username),
'action': {
'module': 'user',
'args': 'name={} shell={} state=present password={}'.format(
system_user.username, system_user.shell,
encrypt_password(system_user.password, salt="K3mIlKK"),
),
}
})
if system_user.public_key:
tasks.append({
'name': 'Set {} authorized key'.format(system_user.username),
'action': {
'module': 'authorized_key',
'args': "user={} state=present key='{}'".format(
system_user.username, system_user.public_key
)
}
})
if system_user.sudo:
sudo = system_user.sudo.replace('\r\n', '\n').replace('\r', '\n')
sudo_list = sudo.split('\n')
sudo_tmp = []
for s in sudo_list:
sudo_tmp.append(s.strip(','))
sudo = ','.join(sudo_tmp)
tasks.append({
'name': 'Set {} sudo setting'.format(system_user.username),
'action': {
'module': 'lineinfile',
'args': "dest=/etc/sudoers state=present regexp='^{0} ALL=' "
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
"validate='visudo -cf %s'".format(
system_user.username, sudo,
)
}
})
return tasks
def get_push_windows_system_user_tasks(system_user):
tasks = []
if not system_user.password:
return tasks
tasks.append({
'name': 'Add user {}'.format(system_user.username),
'action': {
'module': 'win_user',
'args': 'fullname={} '
'name={} '
'password={} '
'state=present '
'update_password=always '
'password_expired=no '
'password_never_expires=yes '
'groups="Users,Remote Desktop Users" '
'groups_action=add '
''.format(system_user.name,
system_user.username,
system_user.password),
}
})
return tasks
def get_push_system_user_tasks(host, system_user):
if host.is_unixlike():
tasks = get_push_linux_system_user_tasks(system_user)
elif host.is_windows():
tasks = get_push_windows_system_user_tasks(system_user)
else:
msg = _(
"The asset {} system platform {} does not "
"support run Ansible tasks".format(host.hostname, host.platform)
)
logger.info(msg)
tasks = []
return tasks
@shared_task(queue="ansible")
def push_system_user_util(system_user, assets, task_name):
from ops.utils import update_or_create_ansible_task
if not system_user.is_need_push():
msg = _("Push system user task skip, auto push not enable or "
"protocol is not ssh or rdp: {}").format(system_user.name)
logger.info(msg)
return {}
# Set root as system user is dangerous
if system_user.username.lower() in ["root", "administrator"]:
msg = _("For security, do not push user {}".format(system_user.username))
logger.info(msg)
return {}
hosts = clean_hosts(assets)
if not hosts:
return {}
hosts = clean_hosts_by_protocol(system_user, hosts)
if not hosts:
return {}
for host in hosts:
system_user.load_specific_asset_auth(host)
tasks = get_push_system_user_tasks(host, system_user)
if not tasks:
continue
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=[host], tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, run_as_admin=True,
created_by=system_user.org_id,
)
task.run()
@shared_task(queue="ansible")
def push_system_user_to_assets_manual(system_user):
assets = system_user.get_all_assets()
task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name=task_name)
@shared_task(queue="ansible")
def push_system_user_a_asset_manual(system_user, asset):
task_name = _("Push system users to asset: {} => {}").format(
system_user.name, asset
)
return push_system_user_util(system_user, [asset], task_name=task_name)
@shared_task(queue="ansible")
def push_system_user_to_assets(system_user, assets):
task_name = _("Push system users to assets: {}").format(system_user.name)
return push_system_user_util(system_user, assets, task_name)
# @shared_task
# @register_as_period_task(interval=3600)
# @after_app_ready_start
# @after_app_shutdown_clean_periodic
# def push_system_user_period():
# for system_user in SystemUser.objects.all():
# push_system_user_related_nodes(system_user)
\ No newline at end of file
from collections import defaultdict
from celery import shared_task
from django.utils.translation import ugettext as _
from common.utils import get_logger
from ..models import SystemUser
from . import const
from .utils import clean_hosts, clean_hosts_by_protocol
logger = get_logger(__name__)
__all__ = [
'test_system_user_connectivity_util', 'test_system_user_connectivity_manual',
'test_system_user_connectivity_period', 'test_system_user_connectivity_a_asset',
]
@shared_task(queue="ansible")
def test_system_user_connectivity_util(system_user, assets, task_name):
"""
Test system cant connect his assets or not.
:param system_user:
:param assets:
:param task_name:
:return:
"""
from ops.utils import update_or_create_ansible_task
hosts = clean_hosts(assets)
if not hosts:
return {}
hosts = clean_hosts_by_protocol(system_user, hosts)
if not hosts:
return {}
hosts_category = {
'linux': {
'hosts': [],
'tasks': const.TEST_SYSTEM_USER_CONN_TASKS
},
'windows': {
'hosts': [],
'tasks': const.TEST_WINDOWS_SYSTEM_USER_CONN_TASKS
}
}
for host in hosts:
hosts_list = hosts_category['windows']['hosts'] if host.is_windows() \
else hosts_category['linux']['hosts']
hosts_list.append(host)
results_summary = dict(
contacted=defaultdict(dict), dark=defaultdict(dict), success=True
)
for k, value in hosts_category.items():
if not value['hosts']:
continue
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=value['hosts'], tasks=value['tasks'],
pattern='all', options=const.TASK_OPTIONS,
run_as=system_user.username, created_by=system_user.org_id,
)
raw, summary = task.run()
success = summary.get('success', False)
contacted = summary.get('contacted', {})
dark = summary.get('dark', {})
results_summary['success'] &= success
results_summary['contacted'].update(contacted)
results_summary['dark'].update(dark)
system_user.set_connectivity(results_summary)
return results_summary
@shared_task(queue="ansible")
def test_system_user_connectivity_manual(system_user):
task_name = _("Test system user connectivity: {}").format(system_user)
assets = system_user.get_all_assets()
return test_system_user_connectivity_util(system_user, assets, task_name)
@shared_task(queue="ansible")
def test_system_user_connectivity_a_asset(system_user, asset):
task_name = _("Test system user connectivity: {} => {}").format(
system_user, asset
)
return test_system_user_connectivity_util(system_user, [asset], task_name)
@shared_task(queue="ansible")
def test_system_user_connectivity_period():
if not const.PERIOD_TASK_ENABLED:
logger.debug("Period task disabled, test system user connectivity pass")
return
system_users = SystemUser.objects.all()
for system_user in system_users:
task_name = _("Test system user connectivity period: {}").format(system_user)
assets = system_user.get_all_assets()
test_system_user_connectivity_util(system_user, assets, task_name)
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext as _
from common.utils import get_logger
logger = get_logger(__file__)
__all__ = [
'check_asset_can_run_ansible', 'clean_hosts', 'clean_hosts_by_protocol'
]
def check_asset_can_run_ansible(asset):
if not asset.is_active:
msg = _("Asset has been disabled, skipped: {}").format(asset)
logger.info(msg)
return False
if not asset.is_support_ansible():
msg = _("Asset may not be support ansible, skipped: {}").format(asset)
logger.info(msg)
return False
return True
def clean_hosts(assets):
clean_assets = []
for asset in assets:
if not check_asset_can_run_ansible(asset):
continue
clean_assets.append(asset)
if not clean_assets:
logger.info(_("No assets matched, stop task"))
return clean_assets
def clean_hosts_by_protocol(system_user, assets):
hosts = [
asset for asset in assets
if asset.has_protocol(system_user.protocol)
]
if not hosts:
msg = _("No assets matched related system user protocol, stop task")
logger.info(msg)
return hosts
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button> <button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels"> <ul class="dropdown-menu labels">
{% for label in labels %} {% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li> <li><a style="font-weight: bolder">{{ label.name }}#{{ label.value }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
...@@ -171,9 +171,13 @@ function initTable() { ...@@ -171,9 +171,13 @@ function initTable() {
], ],
ajax_url: '{% url "api-assets:asset-list" %}', ajax_url: '{% url "api-assets:asset-list" %}',
columns: [ columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "id"}, {data: "hostname"}, {data: "ip"},
{data: "cpu_cores", orderable: false}, {data: "cpu_cores", orderable: false},
{data: "connectivity", orderable: false}, {data: "id", orderable: false } {
data: "connectivity",
orderable: false,
width: '60px'
}, {data: "id", orderable: false}
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
...@@ -271,7 +275,7 @@ $(document).ready(function(){ ...@@ -271,7 +275,7 @@ $(document).ready(function(){
setAssetModalOptions(modalOption); setAssetModalOptions(modalOption);
}) })
.on('click', '.labels li', function () { .on('click', '.labels li', function () {
var val = $(this).text(); var val = 'label:' + $(this).text();
$("#asset_list_table_filter input").val(val); $("#asset_list_table_filter input").val(val);
asset_table.search(val).draw(); asset_table.search(val).draw();
}) })
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
var treeUrl = "{% url 'api-perms:my-nodes-children-as-tree' %}?&cache_policy=1"; var treeUrl = "{% url 'api-perms:my-nodes-children-as-tree' %}?&cache_policy=1";
var assetTableUrl = "{% url 'api-perms:my-assets' %}?cache_policy=1"; var assetTableUrl = "{% url 'api-perms:my-assets' %}?cache_policy=1";
var selectUrl = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1&all=1'; var selectUrl = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1&all=1';
var systemUsersUrl = "{% url 'api-perms:my-asset-system-users' asset_id=DEFAULT_PK %}"; var systemUsersUrl = "{% url 'api-perms:my-asset-system-users' asset_id=DEFAULT_PK %}?cache_policy=1";
var showAssetHref = false; // Need input default true var showAssetHref = false; // Need input default true
var actions = { var actions = {
targets: 4, createdCell: function (td, cellData) { targets: 4, createdCell: function (td, cellData) {
......
...@@ -21,6 +21,7 @@ router.register(r'gateways', api.GatewayViewSet, 'gateway') ...@@ -21,6 +21,7 @@ router.register(r'gateways', api.GatewayViewSet, 'gateway')
router.register(r'cmd-filters', api.CommandFilterViewSet, 'cmd-filter') router.register(r'cmd-filters', api.CommandFilterViewSet, 'cmd-filter')
router.register(r'asset-users', api.AssetUserViewSet, 'asset-user') router.register(r'asset-users', api.AssetUserViewSet, 'asset-user')
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info') router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter') cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule') cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
......
...@@ -24,37 +24,6 @@ def get_system_user_by_id(id): ...@@ -24,37 +24,6 @@ def get_system_user_by_id(id):
return system_user return system_user
class LabelFilterMixin:
def get_filter_labels_ids(self):
query_params = self.request.query_params
query_keys = query_params.keys()
all_label_keys = Label.objects.values_list('name', flat=True)
valid_keys = set(all_label_keys) & set(query_keys)
if not valid_keys:
return []
labels_query = [
{"name": key, "value": query_params[key]}
for key in valid_keys
]
args = [Q(**kwargs) for kwargs in labels_query]
args = reduce(lambda x, y: x | y, args)
labels_id = Label.objects.filter(args).values_list('id', flat=True)
return labels_id
class LabelFilter(LabelFilterMixin):
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
labels_ids = self.get_filter_labels_ids()
if not labels_ids:
return queryset
for labels_id in labels_ids:
queryset = queryset.filter(labels=labels_id)
return queryset
class TreeService(Tree): class TreeService(Tree):
tag_sep = ' / ' tag_sep = ' / '
cache_key = '_NODE_FULL_TREE' cache_key = '_NODE_FULL_TREE'
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import coreapi
from rest_framework import filters from rest_framework import filters
from rest_framework.fields import DateTimeField from rest_framework.fields import DateTimeField
from rest_framework.serializers import ValidationError from rest_framework.serializers import ValidationError
from django.core.cache import cache
import logging import logging
__all__ = ["DatetimeRangeFilter"] from . import const
__all__ = ["DatetimeRangeFilter", "IDSpmFilter", "CustomFilter"]
class DatetimeRangeFilter(filters.BaseFilterBackend): class DatetimeRangeFilter(filters.BaseFilterBackend):
...@@ -40,3 +44,52 @@ class DatetimeRangeFilter(filters.BaseFilterBackend): ...@@ -40,3 +44,52 @@ class DatetimeRangeFilter(filters.BaseFilterBackend):
if kwargs: if kwargs:
queryset = queryset.filter(**kwargs) queryset = queryset.filter(**kwargs)
return queryset return queryset
class IDSpmFilter(filters.BaseFilterBackend):
def get_schema_fields(self, view):
return [
coreapi.Field(
name='spm', location='query', required=False,
type='string', example='',
description='Pre post objects id get spm id, then using filter'
)
]
def filter_queryset(self, request, queryset, view):
spm = request.query_params.get('spm')
if not spm:
return queryset
cache_key = const.KEY_CACHE_RESOURCES_ID.format(spm)
resources_id = cache.get(cache_key)
if not resources_id or not isinstance(resources_id, list):
queryset = queryset.none()
return queryset
queryset = queryset.filter(id__in=resources_id)
return queryset
class CustomFilter(filters.BaseFilterBackend):
def get_schema_fields(self, view):
fields = []
defaults = dict(
location='query', required=False,
type='string', example='',
description=''
)
if not hasattr(view, 'custom_filter_fields'):
return []
for field in view.custom_filter_fields:
if isinstance(field, str):
defaults['name'] = field
elif isinstance(field, dict):
defaults.update(field)
else:
continue
fields.append(coreapi.Field(**defaults))
return fields
def filter_queryset(self, request, queryset, view):
return queryset
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.http import JsonResponse from django.http import JsonResponse
from django.core.cache import cache from rest_framework.settings import api_settings
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from ..const import KEY_CACHE_RESOURCES_ID from ..filters import IDSpmFilter, CustomFilter
__all__ = [ __all__ = [
"JSONResponseMixin", "IDInCacheFilterMixin", "IDExportFilterMixin", "JSONResponseMixin", "CommonApiMixin",
"IDInFilterMixin", "ApiMessageMixin" "IDSpmFilterMixin", "CommonApiMixin",
] ]
...@@ -20,69 +18,31 @@ class JSONResponseMixin(object): ...@@ -20,69 +18,31 @@ class JSONResponseMixin(object):
return JsonResponse(context) return JsonResponse(context)
class IDInFilterMixin(object): class IDSpmFilterMixin:
def filter_queryset(self, queryset): def get_filter_backends(self):
queryset = super(IDInFilterMixin, self).filter_queryset(queryset) backends = super().get_filter_backends()
id_list = self.request.query_params.get('id__in') backends.append(IDSpmFilter)
if id_list: return backends
import json
try:
ids = json.loads(id_list)
except Exception as e:
return queryset
if isinstance(ids, list):
queryset = queryset.filter(id__in=ids)
return queryset
class IDInCacheFilterMixin(object): class ExtraFilterFieldsMixin:
default_added_filters = [CustomFilter, IDSpmFilter]
def filter_queryset(self, queryset): filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
queryset = super().filter_queryset(queryset) extra_filter_fields = []
spm = self.request.query_params.get('spm') extra_filter_backends = []
if not spm:
return queryset
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
resources_id = cache.get(cache_key)
if not resources_id or not isinstance(resources_id, list):
queryset = queryset.none()
return queryset
queryset = queryset.filter(id__in=resources_id)
return queryset
def get_filter_backends(self):
if self.filter_backends != self.__class__.filter_backends:
return self.filter_backends
return list(self.filter_backends) + \
self.default_added_filters + \
list(self.extra_filter_backends)
class IDExportFilterMixin(object):
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
# 下载导入模版 for backend in self.get_filter_backends():
if self.request.query_params.get('template') == 'import': queryset = backend().filter_queryset(self.request, queryset, self)
return [] return queryset
else:
return super(IDExportFilterMixin, self).filter_queryset(queryset)
class ApiMessageMixin:
success_message = _("%(name)s was %(action)s successfully")
_action_map = {"create": _("create"), "update": _("update")}
def get_success_message(self, cleaned_data):
if not isinstance(cleaned_data, dict):
return ''
data = {k: v for k, v in cleaned_data.items()}
action = getattr(self, "action", "create")
data["action"] = self._action_map.get(action)
try:
message = self.success_message % data
except:
message = ''
return message
def dispatch(self, request, *args, **kwargs): class CommonApiMixin(ExtraFilterFieldsMixin):
resp = super().dispatch(request, *args, **kwargs) pass
if request.method.lower() in ("get", "delete", "patch"):
return resp
if resp.status_code >= 400:
return resp
message = self.get_success_message(resp.data)
if message:
messages.success(request, message)
return resp
...@@ -8,7 +8,6 @@ import datetime ...@@ -8,7 +8,6 @@ import datetime
import uuid import uuid
from functools import wraps from functools import wraps
import time import time
import copy
import ipaddress import ipaddress
...@@ -199,3 +198,31 @@ def timeit(func): ...@@ -199,3 +198,31 @@ def timeit(func):
logger.debug(msg) logger.debug(msg)
return result return result
return wrapper return wrapper
def group_obj_by_count(objs, count=50):
objs_grouped = [
objs[i:i + count] for i in range(0, len(objs), count)
]
return objs_grouped
def dict_get_any(d, keys):
for key in keys:
value = d.get(key)
if value:
return value
return None
class lazyproperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, cls):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
return value
\ No newline at end of file
# -*- coding: utf-8 -*-
#
from django.http import HttpResponse
from django.conf import settings
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
from proxy.views import proxy_view
flower_url = settings.FLOWER_URL
@csrf_exempt
def celery_flower_view(request, path):
if not request.user.is_superuser:
return HttpResponse("Forbidden")
remote_url = 'http://{}/{}'.format(flower_url, path)
try:
response = proxy_view(request, remote_url)
except Exception as e:
msg = _("<h1>Flow service unavailable, check it</h1>") + \
'<br><br> <div>{}</div>'.format(e)
response = HttpResponse(msg)
return response
...@@ -382,6 +382,8 @@ defaults = { ...@@ -382,6 +382,8 @@ defaults = {
'SYSLOG_ADDR': '', # '192.168.0.1:514' 'SYSLOG_ADDR': '', # '192.168.0.1:514'
'SYSLOG_FACILITY': 'user', 'SYSLOG_FACILITY': 'user',
'PERM_SINGLE_ASSET_TO_UNGROUP_NODE': False, 'PERM_SINGLE_ASSET_TO_UNGROUP_NODE': False,
'WINDOWS_SSH_DEFAULT_SHELL': 'cmd',
'FLOWER_URL': "127.0.0.1:5555"
} }
......
...@@ -622,3 +622,5 @@ ASSETS_PERM_CACHE_TIME = CONFIG.ASSETS_PERM_CACHE_TIME ...@@ -622,3 +622,5 @@ ASSETS_PERM_CACHE_TIME = CONFIG.ASSETS_PERM_CACHE_TIME
BACKEND_ASSET_USER_AUTH_VAULT = False BACKEND_ASSET_USER_AUTH_VAULT = False
PERM_SINGLE_ASSET_TO_UNGROUP_NODE = CONFIG.PERM_SINGLE_ASSET_TO_UNGROUP_NODE PERM_SINGLE_ASSET_TO_UNGROUP_NODE = CONFIG.PERM_SINGLE_ASSET_TO_UNGROUP_NODE
WINDOWS_SSH_DEFAULT_SHELL = CONFIG.WINDOWS_SSH_DEFAULT_SHELL
FLOWER_URL = CONFIG.FLOWER_URL
...@@ -33,6 +33,21 @@ class CustomSwaggerAutoSchema(SwaggerAutoSchema): ...@@ -33,6 +33,21 @@ class CustomSwaggerAutoSchema(SwaggerAutoSchema):
operation.summary = operation.operation_id operation.summary = operation.operation_id
return operation return operation
def get_filter_parameters(self):
if not self.should_filter():
return []
fields = []
if hasattr(self.view, 'get_filter_backends'):
backends = self.view.get_filter_backends()
elif hasattr(self.view, 'filter_backends'):
backends = self.view.filter_backends
else:
backends = []
for filter_backend in backends:
fields += self.probe_inspectors(self.filter_inspectors, 'get_filter_parameters', filter_backend()) or []
return fields
def get_swagger_view(version='v1'): def get_swagger_view(version='v1'):
from .urls import api_v1, api_v2 from .urls import api_v1, api_v2
......
...@@ -7,7 +7,9 @@ from django.conf.urls.static import static ...@@ -7,7 +7,9 @@ from django.conf.urls.static import static
from django.conf.urls.i18n import i18n_patterns from django.conf.urls.i18n import i18n_patterns
from django.views.i18n import JavaScriptCatalog from django.views.i18n import JavaScriptCatalog
from .views import IndexView, LunaView, I18NView, HealthCheckView, redirect_format_api # from .views import IndexView, LunaView, I18NView, HealthCheckView, redirect_format_api
from . import views
from .celery_flower import celery_flower_view
from .swagger import get_swagger_view from .swagger import get_swagger_view
api_v1 = [ api_v1 = [
...@@ -40,6 +42,7 @@ app_view_patterns = [ ...@@ -40,6 +42,7 @@ app_view_patterns = [
path('orgs/', include('orgs.urls.views_urls', namespace='orgs')), path('orgs/', include('orgs.urls.views_urls', namespace='orgs')),
path('auth/', include('authentication.urls.view_urls'), name='auth'), path('auth/', include('authentication.urls.view_urls'), name='auth'),
path('applications/', include('applications.urls.views_urls', namespace='applications')), path('applications/', include('applications.urls.views_urls', namespace='applications')),
re_path(r'flower/(?P<path>.*)', celery_flower_view, name='flower-view'),
] ]
...@@ -57,13 +60,13 @@ js_i18n_patterns = i18n_patterns( ...@@ -57,13 +60,13 @@ js_i18n_patterns = i18n_patterns(
urlpatterns = [ urlpatterns = [
path('', IndexView.as_view(), name='index'), path('', views.IndexView.as_view(), name='index'),
path('api/v1/', include(api_v1)), path('api/v1/', include(api_v1)),
path('api/v2/', include(api_v2)), path('api/v2/', include(api_v2)),
re_path('api/(?P<app>\w+)/(?P<version>v\d)/.*', redirect_format_api), re_path('api/(?P<app>\w+)/(?P<version>v\d)/.*', views.redirect_format_api),
path('api/health/', HealthCheckView.as_view(), name="health"), path('api/health/', views.HealthCheckView.as_view(), name="health"),
path('luna/', LunaView.as_view(), name='luna-view'), path('luna/', views.LunaView.as_view(), name='luna-view'),
path('i18n/<str:lang>/', I18NView.as_view(), name='i18n-switch'), path('i18n/<str:lang>/', views.I18NView.as_view(), name='i18n-switch'),
path('settings/', include('settings.urls.view_urls', namespace='settings')), path('settings/', include('settings.urls.view_urls', namespace='settings')),
# External apps url # External apps url
......
...@@ -224,3 +224,6 @@ class HealthCheckView(APIView): ...@@ -224,3 +224,6 @@ class HealthCheckView(APIView):
def get(self, request): def get(self, request):
return JsonResponse({"status": 1, "time": int(time.time())}) return JsonResponse({"status": 1, "time": int(time.time())})
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n" "Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-09-12 18:01+0800\n" "POT-Creation-Date: 2019-09-19 19:50+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n" "Language-Team: Jumpserver team<ibuler@qq.com>\n"
...@@ -71,14 +71,14 @@ msgstr "目标地址" ...@@ -71,14 +71,14 @@ msgstr "目标地址"
msgid "Operating parameter" msgid "Operating parameter"
msgstr "运行参数" msgstr "运行参数"
#: applications/forms/remote_app.py:104 applications/models/remote_app.py:23 #: applications/forms/remote_app.py:100 applications/models/remote_app.py:23
#: applications/templates/applications/remote_app_detail.html:57 #: applications/templates/applications/remote_app_detail.html:57
#: applications/templates/applications/remote_app_list.html:22 #: applications/templates/applications/remote_app_list.html:22
#: applications/templates/applications/user_remote_app_list.html:18 #: applications/templates/applications/user_remote_app_list.html:18
#: assets/forms/domain.py:15 assets/forms/label.py:13 #: assets/forms/domain.py:15 assets/forms/label.py:13
#: assets/models/asset.py:295 assets/models/authbook.py:24 #: assets/models/asset.py:295 assets/models/authbook.py:24
#: assets/serializers/admin_user.py:32 assets/serializers/asset_user.py:81 #: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:32
#: assets/serializers/system_user.py:31 #: assets/serializers/asset_user.py:82 assets/serializers/system_user.py:31
#: assets/templates/assets/admin_user_list.html:46 #: assets/templates/assets/admin_user_list.html:46
#: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_detail.html:60
#: assets/templates/assets/domain_list.html:26 #: assets/templates/assets/domain_list.html:26
...@@ -95,41 +95,19 @@ msgstr "运行参数" ...@@ -95,41 +95,19 @@ msgstr "运行参数"
#: terminal/templates/terminal/command_list.html:66 #: terminal/templates/terminal/command_list.html:66
#: terminal/templates/terminal/session_list.html:28 #: terminal/templates/terminal/session_list.html:28
#: terminal/templates/terminal/session_list.html:72 #: terminal/templates/terminal/session_list.html:72
#: xpack/plugins/change_auth_plan/forms.py:121 #: xpack/plugins/change_auth_plan/forms.py:64
#: xpack/plugins/change_auth_plan/models.py:413 #: xpack/plugins/change_auth_plan/models.py:412
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:46
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:54
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:13
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:14
#: xpack/plugins/cloud/models.py:310 #: xpack/plugins/cloud/models.py:307
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:63
#: xpack/plugins/orgs/templates/orgs/org_list.html:16 #: xpack/plugins/orgs/templates/orgs/org_list.html:17
#: xpack/plugins/vault/forms.py:13 xpack/plugins/vault/forms.py:15 #: xpack/plugins/vault/forms.py:13 xpack/plugins/vault/forms.py:15
msgid "Asset" msgid "Asset"
msgstr "资产" msgstr "资产"
#: applications/forms/remote_app.py:107 applications/models/remote_app.py:27
#: applications/templates/applications/remote_app_detail.html:61
#: applications/templates/applications/remote_app_list.html:23
#: applications/templates/applications/user_remote_app_list.html:19
#: assets/models/user.py:168 assets/templates/assets/user_asset_list.html:52
#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52
#: audits/templates/audits/ftp_log_list.html:75
#: perms/forms/asset_permission.py:85 perms/models/asset_permission.py:80
#: perms/templates/perms/asset_permission_detail.html:140
#: perms/templates/perms/asset_permission_list.html:54
#: perms/templates/perms/asset_permission_list.html:75
#: perms/templates/perms/asset_permission_list.html:127 templates/_nav.html:25
#: terminal/backends/command/models.py:14 terminal/models.py:156
#: terminal/templates/terminal/command_list.html:31
#: terminal/templates/terminal/command_list.html:67
#: terminal/templates/terminal/session_list.html:29
#: terminal/templates/terminal/session_list.html:73
#: users/templates/users/_granted_assets.html:27
#: xpack/plugins/orgs/templates/orgs/org_list.html:19
msgid "System user"
msgstr "系统用户"
#: applications/models/remote_app.py:21 #: applications/models/remote_app.py:21
#: applications/templates/applications/remote_app_detail.html:53 #: applications/templates/applications/remote_app_detail.html:53
#: applications/templates/applications/remote_app_list.html:20 #: applications/templates/applications/remote_app_list.html:20
...@@ -166,15 +144,15 @@ msgstr "系统用户" ...@@ -166,15 +144,15 @@ msgstr "系统用户"
#: settings/templates/settings/terminal_setting.html:105 terminal/models.py:22 #: settings/templates/settings/terminal_setting.html:105 terminal/models.py:22
#: terminal/models.py:258 terminal/templates/terminal/terminal_detail.html:43 #: terminal/models.py:258 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14 #: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:330 users/templates/users/_select_user_modal.html:13 #: users/models/user.py:373 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63 #: users/templates/users/user_detail.html:63
#: users/templates/users/user_group_detail.html:55 #: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:35 #: users/templates/users/user_group_list.html:35
#: users/templates/users/user_list.html:35 #: users/templates/users/user_list.html:35
#: users/templates/users/user_profile.html:51 #: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:57 #: users/templates/users/user_pubkey_update.html:57
#: xpack/plugins/change_auth_plan/forms.py:104 #: xpack/plugins/change_auth_plan/forms.py:47
#: xpack/plugins/change_auth_plan/models.py:61 #: xpack/plugins/change_auth_plan/models.py:63
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:61
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:12
#: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144 #: xpack/plugins/cloud/models.py:59 xpack/plugins/cloud/models.py:144
...@@ -182,29 +160,31 @@ msgstr "系统用户" ...@@ -182,29 +160,31 @@ msgstr "系统用户"
#: xpack/plugins/cloud/templates/cloud/account_list.html:12 #: xpack/plugins/cloud/templates/cloud/account_list.html:12
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:56 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:56
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:12
#: xpack/plugins/gathered_user/models.py:28
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:16
#: xpack/plugins/orgs/templates/orgs/org_detail.html:52 #: xpack/plugins/orgs/templates/orgs/org_detail.html:52
#: xpack/plugins/orgs/templates/orgs/org_list.html:12 #: xpack/plugins/orgs/templates/orgs/org_list.html:12
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
#: applications/models/remote_app.py:32 #: applications/models/remote_app.py:28
#: applications/templates/applications/remote_app_detail.html:65 #: applications/templates/applications/remote_app_detail.html:61
#: applications/templates/applications/remote_app_list.html:21 #: applications/templates/applications/remote_app_list.html:21
#: applications/templates/applications/user_remote_app_list.html:17 #: applications/templates/applications/user_remote_app_list.html:17
msgid "App type" msgid "App type"
msgstr "应用类型" msgstr "应用类型"
#: applications/models/remote_app.py:36 #: applications/models/remote_app.py:32
#: applications/templates/applications/remote_app_detail.html:69 #: applications/templates/applications/remote_app_detail.html:65
msgid "App path" msgid "App path"
msgstr "应用路径" msgstr "应用路径"
#: applications/models/remote_app.py:40 #: applications/models/remote_app.py:36
msgid "Parameters" msgid "Parameters"
msgstr "参数" msgstr "参数"
#: applications/models/remote_app.py:43 #: applications/models/remote_app.py:39
#: applications/templates/applications/remote_app_detail.html:77 #: applications/templates/applications/remote_app_detail.html:73
#: assets/models/asset.py:174 assets/models/base.py:36 #: assets/models/asset.py:174 assets/models/base.py:36
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:25 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:25
#: assets/models/cmd_filter.py:58 assets/models/group.py:21 #: assets/models/cmd_filter.py:58 assets/models/group.py:21
...@@ -213,31 +193,32 @@ msgstr "参数" ...@@ -213,31 +193,32 @@ msgstr "参数"
#: assets/templates/assets/cmd_filter_detail.html:77 #: assets/templates/assets/cmd_filter_detail.html:77
#: assets/templates/assets/domain_detail.html:72 #: assets/templates/assets/domain_detail.html:72
#: assets/templates/assets/system_user_detail.html:100 #: assets/templates/assets/system_user_detail.html:100
#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:14 #: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:15
#: perms/models/base.py:54 #: perms/models/base.py:54
#: perms/templates/perms/asset_permission_detail.html:98 #: perms/templates/perms/asset_permission_detail.html:98
#: perms/templates/perms/remote_app_permission_detail.html:90 #: perms/templates/perms/remote_app_permission_detail.html:90
#: users/models/user.py:371 users/serializers/v1.py:119 #: users/models/user.py:414 users/serializers/v1.py:141
#: users/templates/users/user_detail.html:111 #: users/templates/users/user_detail.html:111
#: xpack/plugins/change_auth_plan/models.py:106 #: xpack/plugins/change_auth_plan/models.py:108
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179 #: xpack/plugins/cloud/models.py:80 xpack/plugins/cloud/models.py:179
#: xpack/plugins/gathered_user/models.py:46
msgid "Created by" msgid "Created by"
msgstr "创建者" msgstr "创建者"
# msgid "Created by" # msgid "Created by"
# msgstr "创建者" # msgstr "创建者"
#: applications/models/remote_app.py:46 #: applications/models/remote_app.py:42
#: applications/templates/applications/remote_app_detail.html:73 #: applications/templates/applications/remote_app_detail.html:69
#: assets/models/asset.py:175 assets/models/base.py:34 #: assets/models/asset.py:175 assets/models/base.py:34
#: assets/models/cluster.py:26 assets/models/domain.py:23 #: assets/models/cluster.py:26 assets/models/domain.py:23
#: assets/models/group.py:22 assets/models/label.py:25 #: assets/models/gathered_user.py:19 assets/models/group.py:22
#: assets/templates/assets/admin_user_detail.html:64 #: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/cmd_filter_detail.html:69 #: assets/templates/assets/cmd_filter_detail.html:69
#: assets/templates/assets/domain_detail.html:68 #: assets/templates/assets/domain_detail.html:68
#: assets/templates/assets/system_user_detail.html:96 #: assets/templates/assets/system_user_detail.html:96
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64 #: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64
#: orgs/models.py:15 perms/models/base.py:55 #: orgs/models.py:16 perms/models/base.py:55
#: perms/templates/perms/asset_permission_detail.html:94 #: perms/templates/perms/asset_permission_detail.html:94
#: perms/templates/perms/remote_app_permission_detail.html:86 #: perms/templates/perms/remote_app_permission_detail.html:86
#: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17 #: terminal/templates/terminal/terminal_detail.html:59 users/models/group.py:17
...@@ -252,10 +233,10 @@ msgstr "创建日期" ...@@ -252,10 +233,10 @@ msgstr "创建日期"
# msgid "Date created" # msgid "Date created"
# msgstr "创建日期" # msgstr "创建日期"
#: applications/models/remote_app.py:49 #: applications/models/remote_app.py:45
#: applications/templates/applications/remote_app_detail.html:81 #: applications/templates/applications/remote_app_detail.html:77
#: applications/templates/applications/remote_app_list.html:24 #: applications/templates/applications/remote_app_list.html:23
#: applications/templates/applications/user_remote_app_list.html:20 #: applications/templates/applications/user_remote_app_list.html:19
#: assets/models/asset.py:176 assets/models/base.py:33 #: assets/models/asset.py:176 assets/models/base.py:33
#: assets/models/cluster.py:29 assets/models/cmd_filter.py:22 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:22
#: assets/models/cmd_filter.py:55 assets/models/domain.py:21 #: assets/models/cmd_filter.py:55 assets/models/domain.py:21
...@@ -271,16 +252,16 @@ msgstr "创建日期" ...@@ -271,16 +252,16 @@ msgstr "创建日期"
#: assets/templates/assets/domain_list.html:28 #: assets/templates/assets/domain_list.html:28
#: assets/templates/assets/system_user_detail.html:104 #: assets/templates/assets/system_user_detail.html:104
#: assets/templates/assets/system_user_list.html:59 ops/models/adhoc.py:43 #: assets/templates/assets/system_user_list.html:59 ops/models/adhoc.py:43
#: orgs/models.py:16 perms/models/base.py:56 #: orgs/models.py:17 perms/models/base.py:56
#: perms/templates/perms/asset_permission_detail.html:102 #: perms/templates/perms/asset_permission_detail.html:102
#: perms/templates/perms/remote_app_permission_detail.html:94 #: perms/templates/perms/remote_app_permission_detail.html:94
#: settings/models.py:34 terminal/models.py:32 #: settings/models.py:34 terminal/models.py:32
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 #: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: users/models/user.py:363 users/templates/users/user_detail.html:129 #: users/models/user.py:406 users/templates/users/user_detail.html:129
#: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:37 #: users/templates/users/user_group_list.html:37
#: users/templates/users/user_profile.html:138 #: users/templates/users/user_profile.html:138
#: xpack/plugins/change_auth_plan/models.py:102 #: xpack/plugins/change_auth_plan/models.py:104
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:117
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:19
#: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173 #: xpack/plugins/cloud/models.py:77 xpack/plugins/cloud/models.py:173
...@@ -288,23 +269,24 @@ msgstr "创建日期" ...@@ -288,23 +269,24 @@ msgstr "创建日期"
#: xpack/plugins/cloud/templates/cloud/account_list.html:15 #: xpack/plugins/cloud/templates/cloud/account_list.html:15
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:105 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:105
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:18 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:18
#: xpack/plugins/gathered_user/models.py:42
#: xpack/plugins/orgs/templates/orgs/org_detail.html:64 #: xpack/plugins/orgs/templates/orgs/org_detail.html:64
#: xpack/plugins/orgs/templates/orgs/org_list.html:22 #: xpack/plugins/orgs/templates/orgs/org_list.html:23
msgid "Comment" msgid "Comment"
msgstr "备注" msgstr "备注"
#: applications/models/remote_app.py:53 perms/forms/remote_app_permission.py:37 #: applications/models/remote_app.py:49 perms/forms/remote_app_permission.py:37
#: perms/models/remote_app_permission.py:15 #: perms/models/remote_app_permission.py:15
#: perms/templates/perms/remote_app_permission_create_update.html:48 #: perms/templates/perms/remote_app_permission_create_update.html:48
#: perms/templates/perms/remote_app_permission_detail.html:27 #: perms/templates/perms/remote_app_permission_detail.html:27
#: perms/templates/perms/remote_app_permission_list.html:17 #: perms/templates/perms/remote_app_permission_list.html:17
#: perms/templates/perms/remote_app_permission_remote_app.html:26 #: perms/templates/perms/remote_app_permission_remote_app.html:26
#: perms/templates/perms/remote_app_permission_user.html:26 #: perms/templates/perms/remote_app_permission_user.html:26
#: templates/_nav.html:36 templates/_nav.html:48 templates/_nav_user.html:16 #: templates/_nav.html:60 templates/_nav.html:76 templates/_nav_user.html:16
msgid "RemoteApp" msgid "RemoteApp"
msgstr "远程应用" msgstr "远程应用"
#: applications/templates/applications/remote_app_create_update.html:56 #: applications/templates/applications/remote_app_create_update.html:55
#: assets/templates/assets/_system_user.html:75 #: assets/templates/assets/_system_user.html:75
#: assets/templates/assets/admin_user_create_update.html:45 #: assets/templates/assets/admin_user_create_update.html:45
#: assets/templates/assets/asset_bulk_update.html:23 #: assets/templates/assets/asset_bulk_update.html:23
...@@ -315,7 +297,7 @@ msgstr "远程应用" ...@@ -315,7 +297,7 @@ msgstr "远程应用"
#: assets/templates/assets/gateway_create_update.html:58 #: assets/templates/assets/gateway_create_update.html:58
#: assets/templates/assets/label_create_update.html:18 #: assets/templates/assets/label_create_update.html:18
#: perms/templates/perms/asset_permission_create_update.html:83 #: perms/templates/perms/asset_permission_create_update.html:83
#: perms/templates/perms/remote_app_permission_create_update.html:83 #: perms/templates/perms/remote_app_permission_create_update.html:84
#: settings/templates/settings/basic_setting.html:64 #: settings/templates/settings/basic_setting.html:64
#: settings/templates/settings/command_storage_create.html:79 #: settings/templates/settings/command_storage_create.html:79
#: settings/templates/settings/email_content_setting.html:54 #: settings/templates/settings/email_content_setting.html:54
...@@ -336,12 +318,13 @@ msgstr "远程应用" ...@@ -336,12 +318,13 @@ msgstr "远程应用"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:71 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:71
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:33 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:33
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:44
#: xpack/plugins/interface/templates/interface/interface.html:72 #: xpack/plugins/interface/templates/interface/interface.html:72
#: xpack/plugins/vault/templates/vault/vault_create.html:45 #: xpack/plugins/vault/templates/vault/vault_create.html:45
msgid "Reset" msgid "Reset"
msgstr "重置" msgstr "重置"
#: applications/templates/applications/remote_app_create_update.html:58 #: applications/templates/applications/remote_app_create_update.html:57
#: assets/templates/assets/_system_user.html:76 #: assets/templates/assets/_system_user.html:76
#: assets/templates/assets/admin_user_create_update.html:46 #: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24 #: assets/templates/assets/asset_bulk_update.html:24
...@@ -354,7 +337,7 @@ msgstr "重置" ...@@ -354,7 +337,7 @@ msgstr "重置"
#: assets/templates/assets/label_create_update.html:19 #: assets/templates/assets/label_create_update.html:19
#: audits/templates/audits/login_log_list.html:95 #: audits/templates/audits/login_log_list.html:95
#: perms/templates/perms/asset_permission_create_update.html:84 #: perms/templates/perms/asset_permission_create_update.html:84
#: perms/templates/perms/remote_app_permission_create_update.html:84 #: perms/templates/perms/remote_app_permission_create_update.html:85
#: settings/templates/settings/basic_setting.html:65 #: settings/templates/settings/basic_setting.html:65
#: settings/templates/settings/command_storage_create.html:80 #: settings/templates/settings/command_storage_create.html:80
#: settings/templates/settings/email_content_setting.html:55 #: settings/templates/settings/email_content_setting.html:55
...@@ -401,12 +384,12 @@ msgstr "提交" ...@@ -401,12 +384,12 @@ msgstr "提交"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:20 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:20
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:17
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:106 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:106
#: xpack/plugins/change_auth_plan/views.py:88 #: xpack/plugins/change_auth_plan/views.py:91
msgid "Detail" msgid "Detail"
msgstr "详情" msgstr "详情"
#: applications/templates/applications/remote_app_detail.html:21 #: applications/templates/applications/remote_app_detail.html:21
#: applications/templates/applications/remote_app_list.html:56 #: applications/templates/applications/remote_app_list.html:54
#: assets/templates/assets/_asset_user_list.html:69 #: assets/templates/assets/_asset_user_list.html:69
#: assets/templates/assets/admin_user_detail.html:24 #: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:26 #: assets/templates/assets/admin_user_list.html:26
...@@ -447,13 +430,14 @@ msgstr "详情" ...@@ -447,13 +430,14 @@ msgstr "详情"
#: xpack/plugins/cloud/templates/cloud/account_list.html:40 #: xpack/plugins/cloud/templates/cloud/account_list.html:40
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:29 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:29
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:57 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:57
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:51
#: xpack/plugins/orgs/templates/orgs/org_detail.html:25 #: xpack/plugins/orgs/templates/orgs/org_detail.html:25
#: xpack/plugins/orgs/templates/orgs/org_list.html:88 #: xpack/plugins/orgs/templates/orgs/org_list.html:93
msgid "Update" msgid "Update"
msgstr "更新" msgstr "更新"
#: applications/templates/applications/remote_app_detail.html:25 #: applications/templates/applications/remote_app_detail.html:25
#: applications/templates/applications/remote_app_list.html:57 #: applications/templates/applications/remote_app_list.html:55
#: assets/templates/assets/admin_user_detail.html:28 #: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:112 #: assets/templates/assets/admin_user_list.html:112
#: assets/templates/assets/asset_detail.html:31 #: assets/templates/assets/asset_detail.html:31
...@@ -488,8 +472,9 @@ msgstr "更新" ...@@ -488,8 +472,9 @@ msgstr "更新"
#: xpack/plugins/cloud/templates/cloud/account_list.html:42 #: xpack/plugins/cloud/templates/cloud/account_list.html:42
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:33 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:33
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:58 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:58
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:52
#: xpack/plugins/orgs/templates/orgs/org_detail.html:29 #: xpack/plugins/orgs/templates/orgs/org_detail.html:29
#: xpack/plugins/orgs/templates/orgs/org_list.html:90 #: xpack/plugins/orgs/templates/orgs/org_list.html:95
msgid "Delete" msgid "Delete"
msgstr "删除" msgstr "删除"
...@@ -511,8 +496,8 @@ msgstr "下载应用加载器" ...@@ -511,8 +496,8 @@ msgstr "下载应用加载器"
msgid "Create RemoteApp" msgid "Create RemoteApp"
msgstr "创建远程应用" msgstr "创建远程应用"
#: applications/templates/applications/remote_app_list.html:25 #: applications/templates/applications/remote_app_list.html:24
#: applications/templates/applications/user_remote_app_list.html:21 #: applications/templates/applications/user_remote_app_list.html:20
#: assets/models/cmd_filter.py:54 #: assets/models/cmd_filter.py:54
#: assets/templates/assets/_asset_user_list.html:20 #: assets/templates/assets/_asset_user_list.html:20
#: assets/templates/assets/admin_user_list.html:51 #: assets/templates/assets/admin_user_list.html:51
...@@ -546,11 +531,12 @@ msgstr "创建远程应用" ...@@ -546,11 +531,12 @@ msgstr "创建远程应用"
#: xpack/plugins/cloud/templates/cloud/account_list.html:16 #: xpack/plugins/cloud/templates/cloud/account_list.html:16
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:72 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:72
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:19 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:19
#: xpack/plugins/orgs/templates/orgs/org_list.html:23 #: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:20
#: xpack/plugins/orgs/templates/orgs/org_list.html:24
msgid "Action" msgid "Action"
msgstr "动作" msgstr "动作"
#: applications/templates/applications/user_remote_app_list.html:57 #: applications/templates/applications/user_remote_app_list.html:52
#: assets/templates/assets/user_asset_list.html:32 #: assets/templates/assets/user_asset_list.html:32
#: perms/models/asset_permission.py:30 #: perms/models/asset_permission.py:30
msgid "Connect" msgid "Connect"
...@@ -558,7 +544,7 @@ msgstr "连接" ...@@ -558,7 +544,7 @@ msgstr "连接"
#: applications/views/remote_app.py:31 applications/views/remote_app.py:47 #: applications/views/remote_app.py:31 applications/views/remote_app.py:47
#: applications/views/remote_app.py:70 applications/views/remote_app.py:89 #: applications/views/remote_app.py:70 applications/views/remote_app.py:89
#: templates/_nav.html:33 #: templates/_nav.html:57
msgid "Applications" msgid "Applications"
msgstr "应用管理" msgstr "应用管理"
...@@ -578,11 +564,6 @@ msgstr "远程应用详情" ...@@ -578,11 +564,6 @@ msgstr "远程应用详情"
msgid "My RemoteApp" msgid "My RemoteApp"
msgstr "我的远程应用" msgstr "我的远程应用"
#: assets/api/asset.py:40
#, python-format
msgid "%(hostname)s was %(action)s successfully"
msgstr "%(hostname)s %(action)s成功"
#: assets/api/node.py:58 #: assets/api/node.py:58
msgid "You can't update the root node name" msgid "You can't update the root node name"
msgstr "不能修改根节点名称" msgstr "不能修改根节点名称"
...@@ -595,20 +576,6 @@ msgstr "更新节点资产硬件信息: {}" ...@@ -595,20 +576,6 @@ msgstr "更新节点资产硬件信息: {}"
msgid "Test if the assets under the node are connectable: {}" msgid "Test if the assets under the node are connectable: {}"
msgstr "测试节点下资产是否可连接: {}" msgstr "测试节点下资产是否可连接: {}"
#: assets/const.py:77 assets/models/utils.py:43
msgid "Unreachable"
msgstr "不可达"
#: assets/const.py:78 assets/models/utils.py:44
#: assets/templates/assets/asset_list.html:99
msgid "Reachable"
msgstr "可连接"
#: assets/const.py:79 assets/models/utils.py:45 authentication/utils.py:13
#: xpack/plugins/license/models.py:78
msgid "Unknown"
msgstr "未知"
#: assets/forms/asset.py:24 assets/models/asset.py:140 #: assets/forms/asset.py:24 assets/models/asset.py:140
#: assets/models/domain.py:50 #: assets/models/domain.py:50
#: assets/templates/assets/domain_gateway_list.html:69 #: assets/templates/assets/domain_gateway_list.html:69
...@@ -621,16 +588,18 @@ msgstr "端口" ...@@ -621,16 +588,18 @@ msgstr "端口"
#: assets/templates/assets/asset_detail.html:198 #: assets/templates/assets/asset_detail.html:198
#: assets/templates/assets/system_user_assets.html:83 #: assets/templates/assets/system_user_assets.html:83
#: perms/models/asset_permission.py:79 #: perms/models/asset_permission.py:79
#: xpack/plugins/change_auth_plan/models.py:72 #: xpack/plugins/change_auth_plan/models.py:74
#: xpack/plugins/gathered_user/models.py:31
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:17
msgid "Nodes" msgid "Nodes"
msgstr "节点" msgstr "节点"
#: assets/forms/asset.py:58 assets/forms/asset.py:104 #: assets/forms/asset.py:58 assets/forms/asset.py:104
#: assets/models/asset.py:149 assets/models/cluster.py:19 #: assets/models/asset.py:149 assets/models/cluster.py:19
#: assets/models/user.py:68 assets/templates/assets/asset_detail.html:76 #: assets/models/user.py:68 assets/templates/assets/asset_detail.html:76
#: templates/_nav.html:24 xpack/plugins/cloud/models.py:161 #: templates/_nav.html:44 xpack/plugins/cloud/models.py:161
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:68 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:68
#: xpack/plugins/orgs/templates/orgs/org_list.html:18 #: xpack/plugins/orgs/templates/orgs/org_list.html:19
msgid "Admin user" msgid "Admin user"
msgstr "管理用户" msgstr "管理用户"
...@@ -638,7 +607,7 @@ msgstr "管理用户" ...@@ -638,7 +607,7 @@ msgstr "管理用户"
#: assets/templates/assets/asset_create.html:48 #: assets/templates/assets/asset_create.html:48
#: assets/templates/assets/asset_create.html:50 #: assets/templates/assets/asset_create.html:50
#: assets/templates/assets/asset_list.html:85 #: assets/templates/assets/asset_list.html:85
#: xpack/plugins/orgs/templates/orgs/org_list.html:20 #: xpack/plugins/orgs/templates/orgs/org_list.html:21
msgid "Label" msgid "Label"
msgstr "标签" msgstr "标签"
...@@ -646,18 +615,18 @@ msgstr "标签" ...@@ -646,18 +615,18 @@ msgstr "标签"
#: assets/models/asset.py:144 assets/models/domain.py:26 #: assets/models/asset.py:144 assets/models/domain.py:26
#: assets/models/domain.py:52 assets/templates/assets/asset_detail.html:80 #: assets/models/domain.py:52 assets/templates/assets/asset_detail.html:80
#: assets/templates/assets/user_asset_list.html:53 #: assets/templates/assets/user_asset_list.html:53
#: xpack/plugins/orgs/templates/orgs/org_list.html:17 #: xpack/plugins/orgs/templates/orgs/org_list.html:18
msgid "Domain" msgid "Domain"
msgstr "网域" msgstr "网域"
#: assets/forms/asset.py:68 assets/forms/asset.py:101 assets/forms/asset.py:114 #: assets/forms/asset.py:68 assets/forms/asset.py:101 assets/forms/asset.py:114
#: assets/forms/asset.py:149 assets/models/node.py:393 #: assets/forms/asset.py:149 assets/models/node.py:402
#: assets/templates/assets/asset_create.html:42 #: assets/templates/assets/asset_create.html:42
#: perms/forms/asset_permission.py:82 perms/forms/asset_permission.py:89 #: perms/forms/asset_permission.py:82 perms/forms/asset_permission.py:89
#: perms/templates/perms/asset_permission_list.html:53 #: perms/templates/perms/asset_permission_list.html:53
#: perms/templates/perms/asset_permission_list.html:74 #: perms/templates/perms/asset_permission_list.html:74
#: perms/templates/perms/asset_permission_list.html:124 #: perms/templates/perms/asset_permission_list.html:124
#: xpack/plugins/change_auth_plan/forms.py:122 #: xpack/plugins/change_auth_plan/forms.py:65
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:55
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:15
#: xpack/plugins/cloud/models.py:157 #: xpack/plugins/cloud/models.py:157
...@@ -686,8 +655,8 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域, ...@@ -686,8 +655,8 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
#: assets/forms/asset.py:129 assets/forms/asset.py:133 #: assets/forms/asset.py:129 assets/forms/asset.py:133
#: assets/forms/domain.py:17 assets/forms/label.py:15 #: assets/forms/domain.py:17 assets/forms/label.py:15
#: perms/templates/perms/asset_permission_asset.html:78 #: perms/templates/perms/asset_permission_asset.html:78
#: xpack/plugins/change_auth_plan/forms.py:112 #: xpack/plugins/change_auth_plan/forms.py:55
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:84 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:74
msgid "Select assets" msgid "Select assets"
msgstr "选择资产" msgstr "选择资产"
...@@ -704,7 +673,7 @@ msgid "SSH gateway support proxy SSH,RDP,VNC" ...@@ -704,7 +673,7 @@ msgid "SSH gateway support proxy SSH,RDP,VNC"
msgstr "SSH网关,支持代理SSH,RDP和VNC" msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: assets/forms/domain.py:74 assets/forms/user.py:75 assets/forms/user.py:95 #: assets/forms/domain.py:74 assets/forms/user.py:75 assets/forms/user.py:95
#: assets/models/base.py:29 #: assets/models/base.py:29 assets/models/gathered_user.py:16
#: assets/templates/assets/_asset_user_auth_update_modal.html:15 #: assets/templates/assets/_asset_user_auth_update_modal.html:15
#: assets/templates/assets/_asset_user_auth_view_modal.html:21 #: assets/templates/assets/_asset_user_auth_view_modal.html:21
#: assets/templates/assets/_asset_user_list.html:16 #: assets/templates/assets/_asset_user_list.html:16
...@@ -720,17 +689,18 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" ...@@ -720,17 +689,18 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: perms/templates/perms/asset_permission_user.html:55 #: perms/templates/perms/asset_permission_user.html:55
#: perms/templates/perms/remote_app_permission_user.html:54 #: perms/templates/perms/remote_app_permission_user.html:54
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14 #: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14
#: users/models/user.py:328 users/templates/users/_select_user_modal.html:14 #: users/models/user.py:371 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:67 #: users/templates/users/user_detail.html:67
#: users/templates/users/user_list.html:36 #: users/templates/users/user_list.html:36
#: users/templates/users/user_profile.html:47 #: users/templates/users/user_profile.html:47
#: xpack/plugins/change_auth_plan/forms.py:106 #: xpack/plugins/change_auth_plan/forms.py:49
#: xpack/plugins/change_auth_plan/models.py:63 #: xpack/plugins/change_auth_plan/models.py:65
#: xpack/plugins/change_auth_plan/models.py:409 #: xpack/plugins/change_auth_plan/models.py:408
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:65
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:53
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:12
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:13
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:74
msgid "Username" msgid "Username"
msgstr "用户名" msgstr "用户名"
...@@ -739,7 +709,7 @@ msgid "Password or private key passphrase" ...@@ -739,7 +709,7 @@ msgid "Password or private key passphrase"
msgstr "密码或密钥密码" msgstr "密码或密钥密码"
#: assets/forms/user.py:26 assets/models/base.py:30 #: assets/forms/user.py:26 assets/models/base.py:30
#: assets/serializers/asset_user.py:62 #: assets/serializers/asset_user.py:63
#: assets/templates/assets/_asset_user_auth_update_modal.html:21 #: assets/templates/assets/_asset_user_auth_update_modal.html:21
#: assets/templates/assets/_asset_user_auth_view_modal.html:27 #: assets/templates/assets/_asset_user_auth_view_modal.html:27
#: authentication/forms.py:15 #: authentication/forms.py:15
...@@ -752,14 +722,14 @@ msgstr "密码或密钥密码" ...@@ -752,14 +722,14 @@ msgstr "密码或密钥密码"
#: users/templates/users/user_profile_update.html:41 #: users/templates/users/user_profile_update.html:41
#: users/templates/users/user_pubkey_update.html:41 #: users/templates/users/user_pubkey_update.html:41
#: users/templates/users/user_update.html:20 #: users/templates/users/user_update.html:20
#: xpack/plugins/change_auth_plan/models.py:93 #: xpack/plugins/change_auth_plan/models.py:95
#: xpack/plugins/change_auth_plan/models.py:264 #: xpack/plugins/change_auth_plan/models.py:263
msgid "Password" msgid "Password"
msgstr "密码" msgstr "密码"
#: assets/forms/user.py:29 assets/serializers/asset_user.py:70 #: assets/forms/user.py:29 assets/serializers/asset_user.py:71
#: assets/templates/assets/_asset_user_auth_update_modal.html:27 #: assets/templates/assets/_asset_user_auth_update_modal.html:27
#: users/models/user.py:357 #: users/models/user.py:400
msgid "Private key" msgid "Private key"
msgstr "ssh私钥" msgstr "ssh私钥"
...@@ -810,11 +780,13 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" ...@@ -810,11 +780,13 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: audits/templates/audits/login_log_list.html:60 #: audits/templates/audits/login_log_list.html:60
#: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:140 #: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:140
#: users/templates/users/_granted_assets.html:26 #: users/templates/users/_granted_assets.html:26
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:51 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:54
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:73
msgid "IP" msgid "IP"
msgstr "IP" msgstr "IP"
#: assets/models/asset.py:136 assets/serializers/asset_user.py:27 #: assets/models/asset.py:136 assets/serializers/asset_user.py:27
#: assets/serializers/gathered_user.py:19
#: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/_asset_user_auth_update_modal.html:9 #: assets/templates/assets/_asset_user_auth_update_modal.html:9
#: assets/templates/assets/_asset_user_auth_view_modal.html:15 #: assets/templates/assets/_asset_user_auth_view_modal.html:15
...@@ -825,7 +797,8 @@ msgstr "IP" ...@@ -825,7 +797,8 @@ msgstr "IP"
#: perms/templates/perms/asset_permission_asset.html:57 #: perms/templates/perms/asset_permission_asset.html:57
#: perms/templates/perms/asset_permission_list.html:73 settings/forms.py:139 #: perms/templates/perms/asset_permission_list.html:73 settings/forms.py:139
#: users/templates/users/_granted_assets.html:25 #: users/templates/users/_granted_assets.html:25
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:50 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:53
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:72
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
...@@ -841,7 +814,7 @@ msgstr "协议" ...@@ -841,7 +814,7 @@ msgstr "协议"
#: assets/models/asset.py:142 assets/serializers/asset.py:63 #: assets/models/asset.py:142 assets/serializers/asset.py:63
#: assets/templates/assets/asset_create.html:24 #: assets/templates/assets/asset_create.html:24
#: assets/templates/assets/user_asset_list.html:50 #: assets/templates/assets/user_asset_list.html:50
#: perms/serializers/user_permission.py:38 #: perms/serializers/user_permission.py:48
msgid "Protocols" msgid "Protocols"
msgstr "协议组" msgstr "协议组"
...@@ -850,9 +823,9 @@ msgstr "协议组" ...@@ -850,9 +823,9 @@ msgstr "协议组"
msgid "Platform" msgid "Platform"
msgstr "系统平台" msgstr "系统平台"
#: assets/models/asset.py:146 assets/models/cmd_filter.py:21 #: assets/models/asset.py:146 assets/models/authbook.py:27
#: assets/models/domain.py:54 assets/models/label.py:22 #: assets/models/cmd_filter.py:21 assets/models/domain.py:54
#: assets/templates/assets/asset_detail.html:112 #: assets/models/label.py:22 assets/templates/assets/asset_detail.html:112
msgid "Is active" msgid "Is active"
msgstr "激活" msgstr "激活"
...@@ -922,7 +895,7 @@ msgid "Hostname raw" ...@@ -922,7 +895,7 @@ msgid "Hostname raw"
msgstr "主机名原始" msgstr "主机名原始"
#: assets/models/asset.py:173 assets/templates/assets/asset_create.html:46 #: assets/models/asset.py:173 assets/templates/assets/asset_create.html:46
#: assets/templates/assets/asset_detail.html:224 templates/_nav.html:26 #: assets/templates/assets/asset_detail.html:224 templates/_nav.html:46
msgid "Labels" msgid "Labels"
msgstr "标签管理" msgstr "标签管理"
...@@ -938,22 +911,24 @@ msgstr "最新版本" ...@@ -938,22 +911,24 @@ msgstr "最新版本"
msgid "Version" msgid "Version"
msgstr "版本" msgstr "版本"
#: assets/models/authbook.py:35 #: assets/models/authbook.py:36
msgid "AuthBook" msgid "AuthBook"
msgstr "" msgstr ""
#: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:97 #: assets/models/base.py:31 xpack/plugins/change_auth_plan/models.py:99
#: xpack/plugins/change_auth_plan/models.py:271 #: xpack/plugins/change_auth_plan/models.py:270
msgid "SSH private key" msgid "SSH private key"
msgstr "ssh密钥" msgstr "ssh密钥"
#: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:100 #: assets/models/base.py:32 xpack/plugins/change_auth_plan/models.py:102
#: xpack/plugins/change_auth_plan/models.py:267 #: xpack/plugins/change_auth_plan/models.py:266
msgid "SSH public key" msgid "SSH public key"
msgstr "ssh公钥" msgstr "ssh公钥"
#: assets/models/base.py:35 assets/templates/assets/cmd_filter_detail.html:73 #: assets/models/base.py:35 assets/models/gathered_user.py:21
#: assets/templates/assets/cmd_filter_detail.html:73
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:76
msgid "Date updated" msgid "Date updated"
msgstr "更新日期" msgstr "更新日期"
...@@ -965,7 +940,7 @@ msgstr "带宽" ...@@ -965,7 +940,7 @@ msgstr "带宽"
msgid "Contact" msgid "Contact"
msgstr "联系人" msgstr "联系人"
#: assets/models/cluster.py:22 users/models/user.py:349 #: assets/models/cluster.py:22 users/models/user.py:392
#: users/templates/users/user_detail.html:76 #: users/templates/users/user_detail.html:76
msgid "Phone" msgid "Phone"
msgstr "手机" msgstr "手机"
...@@ -991,7 +966,7 @@ msgid "Default" ...@@ -991,7 +966,7 @@ msgid "Default"
msgstr "默认" msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14 #: assets/models/cluster.py:36 assets/models/label.py:14
#: users/models/user.py:469 #: users/models/user.py:512
msgid "System" msgid "System"
msgstr "系统" msgstr "系统"
...@@ -1081,6 +1056,15 @@ msgstr "命令过滤规则" ...@@ -1081,6 +1056,15 @@ msgstr "命令过滤规则"
msgid "Gateway" msgid "Gateway"
msgstr "网关" msgstr "网关"
#: assets/models/gathered_user.py:17
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:75
msgid "Present"
msgstr "存在"
#: assets/models/gathered_user.py:32
msgid "GatherUser"
msgstr "收集用户"
#: assets/models/group.py:30 #: assets/models/group.py:30
msgid "Asset group" msgid "Asset group"
msgstr "资产组" msgstr "资产组"
...@@ -1110,16 +1094,16 @@ msgstr "默认资产组" ...@@ -1110,16 +1094,16 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:65 #: terminal/templates/terminal/command_list.html:65
#: terminal/templates/terminal/session_list.html:27 #: terminal/templates/terminal/session_list.html:27
#: terminal/templates/terminal/session_list.html:71 users/forms.py:316 #: terminal/templates/terminal/session_list.html:71 users/forms.py:316
#: users/models/user.py:127 users/models/user.py:457 #: users/models/user.py:127 users/models/user.py:143 users/models/user.py:500
#: users/serializers/v1.py:108 users/templates/users/user_group_detail.html:78 #: users/serializers/v1.py:130 users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:36 users/views/user.py:243 #: users/templates/users/user_group_list.html:36 users/views/user.py:250
#: xpack/plugins/orgs/forms.py:26 #: xpack/plugins/orgs/forms.py:26
#: xpack/plugins/orgs/templates/orgs/org_detail.html:113 #: xpack/plugins/orgs/templates/orgs/org_detail.html:113
#: xpack/plugins/orgs/templates/orgs/org_list.html:14 #: xpack/plugins/orgs/templates/orgs/org_list.html:15
msgid "User" msgid "User"
msgstr "用户" msgstr "用户"
#: assets/models/label.py:19 assets/models/node.py:384 #: assets/models/label.py:19 assets/models/node.py:393
#: assets/templates/assets/label_list.html:15 settings/models.py:30 #: assets/templates/assets/label_list.html:15 settings/models.py:30
msgid "Value" msgid "Value"
msgstr "值" msgstr "值"
...@@ -1128,19 +1112,19 @@ msgstr "值" ...@@ -1128,19 +1112,19 @@ msgstr "值"
msgid "Category" msgid "Category"
msgstr "分类" msgstr "分类"
#: assets/models/node.py:222 #: assets/models/node.py:230
msgid "New node" msgid "New node"
msgstr "新节点" msgstr "新节点"
#: assets/models/node.py:308 perms/api/mixin.py:146 #: assets/models/node.py:317
msgid "ungrouped" msgid "ungrouped"
msgstr "未分组" msgstr "未分组"
#: assets/models/node.py:310 perms/api/mixin.py:151 #: assets/models/node.py:319
msgid "empty" msgid "empty"
msgstr "空" msgstr "空"
#: assets/models/node.py:383 #: assets/models/node.py:392
msgid "Key" msgid "Key"
msgstr "键" msgstr "键"
...@@ -1171,7 +1155,7 @@ msgstr "手动登录" ...@@ -1171,7 +1155,7 @@ msgstr "手动登录"
#: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73 #: assets/views/label.py:27 assets/views/label.py:45 assets/views/label.py:73
#: assets/views/system_user.py:29 assets/views/system_user.py:46 #: assets/views/system_user.py:29 assets/views/system_user.py:46
#: assets/views/system_user.py:63 assets/views/system_user.py:79 #: assets/views/system_user.py:63 assets/views/system_user.py:79
#: templates/_nav.html:19 xpack/plugins/change_auth_plan/models.py:68 #: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:70
msgid "Assets" msgid "Assets"
msgstr "资产管理" msgstr "资产管理"
...@@ -1194,11 +1178,45 @@ msgstr "Shell" ...@@ -1194,11 +1178,45 @@ msgstr "Shell"
msgid "Login mode" msgid "Login mode"
msgstr "登录模式" msgstr "登录模式"
#: assets/models/user.py:168 assets/templates/assets/user_asset_list.html:52
#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52
#: audits/templates/audits/ftp_log_list.html:75
#: perms/forms/asset_permission.py:85 perms/forms/remote_app_permission.py:40
#: perms/models/asset_permission.py:80 perms/models/remote_app_permission.py:16
#: perms/templates/perms/asset_permission_detail.html:140
#: perms/templates/perms/asset_permission_list.html:54
#: perms/templates/perms/asset_permission_list.html:75
#: perms/templates/perms/asset_permission_list.html:127
#: perms/templates/perms/remote_app_permission_detail.html:131
#: templates/_nav.html:45 terminal/backends/command/models.py:14
#: terminal/models.py:156 terminal/templates/terminal/command_list.html:31
#: terminal/templates/terminal/command_list.html:67
#: terminal/templates/terminal/session_list.html:29
#: terminal/templates/terminal/session_list.html:73
#: users/templates/users/_granted_assets.html:27
#: xpack/plugins/orgs/templates/orgs/org_list.html:20
msgid "System user"
msgstr "系统用户"
#: assets/models/utils.py:35 #: assets/models/utils.py:35
#, python-format #, python-format
msgid "%(value)s is not an even number" msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number" msgstr "%(value)s is not an even number"
#: assets/models/utils.py:43 assets/tasks/const.py:81
msgid "Unreachable"
msgstr "不可达"
#: assets/models/utils.py:44 assets/tasks/const.py:82
#: assets/templates/assets/asset_list.html:99
msgid "Reachable"
msgstr "可连接"
#: assets/models/utils.py:45 assets/tasks/const.py:83
#: authentication/utils.py:13 xpack/plugins/license/models.py:78
msgid "Unknown"
msgstr "未知"
#: assets/serializers/asset.py:21 #: assets/serializers/asset.py:21
msgid "Protocol format should {}/{}" msgid "Protocol format should {}/{}"
msgstr "协议格式 {}/{}" msgstr "协议格式 {}/{}"
...@@ -1216,7 +1234,7 @@ msgstr "连接" ...@@ -1216,7 +1234,7 @@ msgstr "连接"
msgid "Hardware info" msgid "Hardware info"
msgstr "硬件信息" msgstr "硬件信息"
#: assets/serializers/asset.py:91 orgs/mixins/serializers.py:26 #: assets/serializers/asset.py:91 orgs/mixins/serializers.py:27
msgid "Org name" msgid "Org name"
msgstr "组织名称" msgstr "组织名称"
...@@ -1224,8 +1242,8 @@ msgstr "组织名称" ...@@ -1224,8 +1242,8 @@ msgstr "组织名称"
msgid "Backend" msgid "Backend"
msgstr "后端" msgstr "后端"
#: assets/serializers/asset_user.py:66 users/forms.py:263 #: assets/serializers/asset_user.py:67 users/forms.py:263
#: users/models/user.py:360 users/templates/users/first_login.html:42 #: users/models/user.py:403 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:49 #: users/templates/users/user_password_update.html:49
#: users/templates/users/user_profile.html:69 #: users/templates/users/user_profile.html:69
#: users/templates/users/user_profile_update.html:46 #: users/templates/users/user_profile_update.html:46
...@@ -1262,88 +1280,93 @@ msgstr "自动登录模式,必须填写用户名" ...@@ -1262,88 +1280,93 @@ msgstr "自动登录模式,必须填写用户名"
msgid "Password or private key required" msgid "Password or private key required"
msgstr "密码或密钥密码需要一个" msgstr "密码或密钥密码需要一个"
#: assets/tasks.py:33 #: assets/tasks/admin_user_connectivity.py:56
msgid "Asset has been disabled, skipped: {}" msgid "Test admin user connectivity period: {}"
msgstr "资产或许不支持ansible, 跳过: {}" msgstr "定期测试管理账号可连接性: {}"
#: assets/tasks.py:37 #: assets/tasks/admin_user_connectivity.py:63
msgid "Asset may not be support ansible, skipped: {}" msgid "Test admin user connectivity: {}"
msgstr "资产或许不支持ansible, 跳过: {}" msgstr "测试管理行号可连接性: {}"
#: assets/tasks.py:50 #: assets/tasks/asset_connectivity.py:21
msgid "No assets matched, stop task" msgid "Test assets connectivity"
msgstr "没有匹配到资产,结束任务" msgstr "测试资产可连接性"
#: assets/tasks.py:60 #: assets/tasks/asset_connectivity.py:75
msgid "No assets matched related system user protocol, stop task" msgid "Test assets connectivity: {}"
msgstr "没有匹配到与系统用户协议相关的资产,结束任务" msgstr "测试资产可连接性: {}"
#: assets/tasks/asset_user_connectivity.py:27
#: assets/tasks/push_system_user.py:130
#: xpack/plugins/change_auth_plan/models.py:521
msgid "The asset {} system platform {} does not support run Ansible tasks"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
#: assets/tasks.py:86 #: assets/tasks/asset_user_connectivity.py:74
msgid "Test asset user connectivity: {}"
msgstr "测试资产用户可连接性: {}"
#: assets/tasks/gather_asset_hardware_info.py:44
msgid "Get asset info failed: {}" msgid "Get asset info failed: {}"
msgstr "获取资产信息失败:{}" msgstr "获取资产信息失败:{}"
#: assets/tasks.py:136 #: assets/tasks/gather_asset_hardware_info.py:94
msgid "Update some assets hardware info" msgid "Update some assets hardware info"
msgstr "更新资产硬件信息" msgstr "更新资产硬件信息"
#: assets/tasks.py:153 #: assets/tasks/gather_asset_hardware_info.py:111
msgid "Update asset hardware info: {}" msgid "Update asset hardware info: {}"
msgstr "更新资产硬件信息: {}" msgstr "更新资产硬件信息: {}"
#: assets/tasks.py:178 #: assets/tasks/gather_asset_users.py:96
msgid "Test assets connectivity" msgid "Gather assets users"
msgstr "测试资产可连接性" msgstr "收集资产上的用户"
#: assets/tasks.py:232 #: assets/tasks/push_system_user.py:142
msgid "Test assets connectivity: {}" msgid ""
msgstr "测试资产可连接性: {}" "Push system user task skip, auto push not enable or protocol is not ssh or "
"rdp: {}"
msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}"
#: assets/tasks.py:274 #: assets/tasks/push_system_user.py:149
msgid "Test admin user connectivity period: {}" msgid "For security, do not push user {}"
msgstr "定期测试管理账号可连接性: {}" msgstr "为了安全,禁止推送用户 {}"
#: assets/tasks.py:281 #: assets/tasks/push_system_user.py:177 assets/tasks/push_system_user.py:191
msgid "Test admin user connectivity: {}" msgid "Push system users to assets: {}"
msgstr "测试管理行号可连接性: {}" msgstr "推送系统用户到入资产: {}"
#: assets/tasks/push_system_user.py:183
msgid "Push system users to asset: {} => {}"
msgstr "推送系统用户到入资产: {} => {}"
#: assets/tasks.py:349 #: assets/tasks/system_user_connectivity.py:79
msgid "Test system user connectivity: {}" msgid "Test system user connectivity: {}"
msgstr "测试系统用户可连接性: {}" msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:356 #: assets/tasks/system_user_connectivity.py:86
msgid "Test system user connectivity: {} => {}" msgid "Test system user connectivity: {} => {}"
msgstr "测试系统用户可连接性: {} => {}" msgstr "测试系统用户可连接性: {} => {}"
#: assets/tasks.py:369 #: assets/tasks/system_user_connectivity.py:99
msgid "Test system user connectivity period: {}" msgid "Test system user connectivity period: {}"
msgstr "定期测试系统用户可连接性: {}" msgstr "定期测试系统用户可连接性: {}"
#: assets/tasks.py:487 assets/tasks.py:573 #: assets/tasks/utils.py:16
#: xpack/plugins/change_auth_plan/models.py:522 msgid "Asset has been disabled, skipped: {}"
msgid "The asset {} system platform {} does not support run Ansible tasks" msgstr "资产或许不支持ansible, 跳过: {}"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
#: assets/tasks.py:499
msgid ""
"Push system user task skip, auto push not enable or protocol is not ssh or "
"rdp: {}"
msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}"
#: assets/tasks.py:506
msgid "For security, do not push user {}"
msgstr "为了安全,禁止推送用户 {}"
#: assets/tasks.py:534 assets/tasks.py:548 #: assets/tasks/utils.py:20
msgid "Push system users to assets: {}" msgid "Asset may not be support ansible, skipped: {}"
msgstr "推送系统用户到入资产: {}" msgstr "资产或许不支持ansible, 跳过: {}"
#: assets/tasks.py:540 #: assets/tasks/utils.py:33
msgid "Push system users to asset: {} => {}" msgid "No assets matched, stop task"
msgstr "推送系统用户到入资产: {} => {}" msgstr "没有匹配到资产,结束任务"
#: assets/tasks.py:620 #: assets/tasks/utils.py:43
msgid "Test asset user connectivity: {}" msgid "No assets matched related system user protocol, stop task"
msgstr "测试资产用户可连接性: {}" msgstr "没有匹配到与系统用户协议相关的资产,结束任务"
#: assets/templates/assets/_admin_user_import_modal.html:4 #: assets/templates/assets/_admin_user_import_modal.html:4
msgid "Import admin user" msgid "Import admin user"
...@@ -1385,14 +1408,14 @@ msgid "Import assets" ...@@ -1385,14 +1408,14 @@ msgid "Import assets"
msgstr "导入资产" msgstr "导入资产"
#: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:38 #: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:38
#: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:115 #: templates/_nav.html:42 xpack/plugins/change_auth_plan/views.py:118
msgid "Asset list" msgid "Asset list"
msgstr "资产列表" msgstr "资产列表"
#: assets/templates/assets/_asset_list_modal.html:33 #: assets/templates/assets/_asset_list_modal.html:33
#: assets/templates/assets/_node_tree.html:40 #: assets/templates/assets/_node_tree.html:40
#: ops/templates/ops/command_execution_create.html:49
#: users/templates/users/_granted_assets.html:7 #: users/templates/users/_granted_assets.html:7
#: users/templates/users/_granted_assets.html:83
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:66
msgid "Loading" msgid "Loading"
msgstr "加载中" msgstr "加载中"
...@@ -1406,14 +1429,14 @@ msgid "Update asset user auth" ...@@ -1406,14 +1429,14 @@ msgid "Update asset user auth"
msgstr "更新资产用户认证信息" msgstr "更新资产用户认证信息"
#: assets/templates/assets/_asset_user_auth_update_modal.html:23 #: assets/templates/assets/_asset_user_auth_update_modal.html:23
#: xpack/plugins/change_auth_plan/forms.py:108 #: xpack/plugins/change_auth_plan/forms.py:51
msgid "Please input password" msgid "Please input password"
msgstr "请输入密码" msgstr "请输入密码"
#: assets/templates/assets/_asset_user_auth_update_modal.html:68 #: assets/templates/assets/_asset_user_auth_update_modal.html:68
#: assets/templates/assets/asset_detail.html:306 #: assets/templates/assets/asset_detail.html:306
#: users/templates/users/user_detail.html:311 #: users/templates/users/user_detail.html:313
#: users/templates/users/user_detail.html:338 #: users/templates/users/user_detail.html:340
#: xpack/plugins/interface/views.py:35 #: xpack/plugins/interface/views.py:35
msgid "Update successfully!" msgid "Update successfully!"
msgstr "更新成功" msgstr "更新成功"
...@@ -1519,6 +1542,7 @@ msgstr "重命名成功" ...@@ -1519,6 +1542,7 @@ msgstr "重命名成功"
#: perms/templates/perms/remote_app_permission_create_update.html:39 #: perms/templates/perms/remote_app_permission_create_update.html:39
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:43 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:43
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:27 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:27
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:27
msgid "Basic" msgid "Basic"
msgstr "基本" msgstr "基本"
...@@ -1537,10 +1561,11 @@ msgstr "自动生成密钥" ...@@ -1537,10 +1561,11 @@ msgstr "自动生成密钥"
#: assets/templates/assets/asset_create.html:74 #: assets/templates/assets/asset_create.html:74
#: assets/templates/assets/gateway_create_update.html:53 #: assets/templates/assets/gateway_create_update.html:53
#: perms/templates/perms/asset_permission_create_update.html:53 #: perms/templates/perms/asset_permission_create_update.html:53
#: perms/templates/perms/remote_app_permission_create_update.html:52 #: perms/templates/perms/remote_app_permission_create_update.html:53
#: terminal/templates/terminal/terminal_update.html:40 #: terminal/templates/terminal/terminal_update.html:40
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:67 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:67
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:48 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:48
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:39
msgid "Other" msgid "Other"
msgstr "其它" msgstr "其它"
...@@ -1590,14 +1615,15 @@ msgstr "替换资产的管理员" ...@@ -1590,14 +1615,15 @@ msgstr "替换资产的管理员"
#: assets/templates/assets/admin_user_detail.html:91 #: assets/templates/assets/admin_user_detail.html:91
#: perms/templates/perms/asset_permission_asset.html:103 #: perms/templates/perms/asset_permission_asset.html:103
#: xpack/plugins/change_auth_plan/forms.py:116 #: xpack/plugins/change_auth_plan/forms.py:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:109 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:99
#: xpack/plugins/gathered_user/forms.py:36
msgid "Select nodes" msgid "Select nodes"
msgstr "选择节点" msgstr "选择节点"
#: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:204 #: assets/templates/assets/asset_detail.html:204
#: assets/templates/assets/asset_list.html:418 #: assets/templates/assets/asset_list.html:422
#: assets/templates/assets/cmd_filter_detail.html:106 #: assets/templates/assets/cmd_filter_detail.html:106
#: assets/templates/assets/system_user_assets.html:97 #: assets/templates/assets/system_user_assets.html:97
#: assets/templates/assets/system_user_detail.html:182 #: assets/templates/assets/system_user_detail.html:182
...@@ -1605,17 +1631,18 @@ msgstr "选择节点" ...@@ -1605,17 +1631,18 @@ msgstr "选择节点"
#: authentication/templates/authentication/_mfa_confirm_modal.html:20 #: authentication/templates/authentication/_mfa_confirm_modal.html:20
#: settings/templates/settings/terminal_setting.html:168 #: settings/templates/settings/terminal_setting.html:168
#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108 #: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108
#: users/templates/users/user_detail.html:392 #: users/templates/users/user_detail.html:394
#: users/templates/users/user_detail.html:418 #: users/templates/users/user_detail.html:420
#: users/templates/users/user_detail.html:441 #: users/templates/users/user_detail.html:443
#: users/templates/users/user_detail.html:486 #: users/templates/users/user_detail.html:488
#: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_list.html:120 #: users/templates/users/user_group_list.html:120
#: users/templates/users/user_list.html:256 #: users/templates/users/user_list.html:256
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:34 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:34
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:54 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:54
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:45
#: xpack/plugins/interface/templates/interface/interface.html:103 #: xpack/plugins/interface/templates/interface/interface.html:103
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:34
msgid "Confirm" msgid "Confirm"
msgstr "确认" msgstr "确认"
...@@ -1638,6 +1665,7 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件 ...@@ -1638,6 +1665,7 @@ msgstr "Jumpserver 使用该用户来 `推送系统用户`、`获取资产硬件
#: audits/templates/audits/login_log_list.html:91 #: audits/templates/audits/login_log_list.html:91
#: users/templates/users/user_group_list.html:10 #: users/templates/users/user_group_list.html:10
#: users/templates/users/user_list.html:10 #: users/templates/users/user_list.html:10
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:59
#: xpack/plugins/vault/templates/vault/vault.html:55 #: xpack/plugins/vault/templates/vault/vault.html:55
msgid "Export" msgid "Export"
msgstr "导出" msgstr "导出"
...@@ -1660,8 +1688,8 @@ msgstr "创建管理用户" ...@@ -1660,8 +1688,8 @@ msgstr "创建管理用户"
#: assets/templates/assets/admin_user_list.html:162 #: assets/templates/assets/admin_user_list.html:162
#: assets/templates/assets/admin_user_list.html:193 #: assets/templates/assets/admin_user_list.html:193
#: assets/templates/assets/asset_list.html:299 #: assets/templates/assets/asset_list.html:303
#: assets/templates/assets/asset_list.html:336 #: assets/templates/assets/asset_list.html:340
#: assets/templates/assets/system_user_list.html:192 #: assets/templates/assets/system_user_list.html:192
#: assets/templates/assets/system_user_list.html:223 #: assets/templates/assets/system_user_list.html:223
#: users/templates/users/user_group_list.html:164 #: users/templates/users/user_group_list.html:164
...@@ -1721,7 +1749,7 @@ msgstr "创建日期" ...@@ -1721,7 +1749,7 @@ msgstr "创建日期"
#: perms/models/base.py:51 #: perms/models/base.py:51
#: perms/templates/perms/asset_permission_create_update.html:55 #: perms/templates/perms/asset_permission_create_update.html:55
#: perms/templates/perms/asset_permission_detail.html:120 #: perms/templates/perms/asset_permission_detail.html:120
#: perms/templates/perms/remote_app_permission_create_update.html:54 #: perms/templates/perms/remote_app_permission_create_update.html:55
#: perms/templates/perms/remote_app_permission_detail.html:112 #: perms/templates/perms/remote_app_permission_detail.html:112
#: terminal/templates/terminal/terminal_list.html:34 #: terminal/templates/terminal/terminal_list.html:34
#: users/templates/users/_select_user_modal.html:18 #: users/templates/users/_select_user_modal.html:18
...@@ -1779,69 +1807,69 @@ msgstr "禁用所选" ...@@ -1779,69 +1807,69 @@ msgstr "禁用所选"
msgid "Active selected" msgid "Active selected"
msgstr "激活所选" msgstr "激活所选"
#: assets/templates/assets/asset_list.html:189 #: assets/templates/assets/asset_list.html:193
msgid "Add assets to node" msgid "Add assets to node"
msgstr "添加资产到节点" msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:190 #: assets/templates/assets/asset_list.html:194
msgid "Move assets to node" msgid "Move assets to node"
msgstr "移动资产到节点" msgstr "移动资产到节点"
#: assets/templates/assets/asset_list.html:192 #: assets/templates/assets/asset_list.html:196
msgid "Refresh node hardware info" msgid "Refresh node hardware info"
msgstr "更新节点资产硬件信息" msgstr "更新节点资产硬件信息"
#: assets/templates/assets/asset_list.html:193 #: assets/templates/assets/asset_list.html:197
msgid "Test node connective" msgid "Test node connective"
msgstr "测试节点资产可连接性" msgstr "测试节点资产可连接性"
#: assets/templates/assets/asset_list.html:195 #: assets/templates/assets/asset_list.html:199
msgid "Display only current node assets" msgid "Display only current node assets"
msgstr "仅显示当前节点资产" msgstr "仅显示当前节点资产"
#: assets/templates/assets/asset_list.html:196 #: assets/templates/assets/asset_list.html:200
msgid "Displays all child node assets" msgid "Displays all child node assets"
msgstr "显示所有子节点资产" msgstr "显示所有子节点资产"
#: assets/templates/assets/asset_list.html:412 #: assets/templates/assets/asset_list.html:416
#: assets/templates/assets/system_user_list.html:133 #: assets/templates/assets/system_user_list.html:133
#: users/templates/users/user_detail.html:386 #: users/templates/users/user_detail.html:388
#: users/templates/users/user_detail.html:412 #: users/templates/users/user_detail.html:414
#: users/templates/users/user_detail.html:480 #: users/templates/users/user_detail.html:482
#: users/templates/users/user_group_list.html:114 #: users/templates/users/user_group_list.html:114
#: users/templates/users/user_list.html:250 #: users/templates/users/user_list.html:250
#: xpack/plugins/interface/templates/interface/interface.html:97 #: xpack/plugins/interface/templates/interface/interface.html:97
msgid "Are you sure?" msgid "Are you sure?"
msgstr "你确认吗?" msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:413 #: assets/templates/assets/asset_list.html:417
msgid "This will delete the selected assets !!!" msgid "This will delete the selected assets !!!"
msgstr "删除选择资产" msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:416 #: assets/templates/assets/asset_list.html:420
#: assets/templates/assets/system_user_list.html:137 #: assets/templates/assets/system_user_list.html:137
#: settings/templates/settings/terminal_setting.html:166 #: settings/templates/settings/terminal_setting.html:166
#: users/templates/users/user_detail.html:390 #: users/templates/users/user_detail.html:392
#: users/templates/users/user_detail.html:416 #: users/templates/users/user_detail.html:418
#: users/templates/users/user_detail.html:484 #: users/templates/users/user_detail.html:486
#: users/templates/users/user_group_create_update.html:31 #: users/templates/users/user_group_create_update.html:31
#: users/templates/users/user_group_list.html:118 #: users/templates/users/user_group_list.html:118
#: users/templates/users/user_list.html:254 #: users/templates/users/user_list.html:254
#: xpack/plugins/interface/templates/interface/interface.html:101 #: xpack/plugins/interface/templates/interface/interface.html:101
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:32 #: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: assets/templates/assets/asset_list.html:429 #: assets/templates/assets/asset_list.html:433
msgid "Asset Deleted." msgid "Asset Deleted."
msgstr "已被删除" msgstr "已被删除"
#: assets/templates/assets/asset_list.html:430
#: assets/templates/assets/asset_list.html:434 #: assets/templates/assets/asset_list.html:434
#: assets/templates/assets/asset_list.html:438
msgid "Asset Delete" msgid "Asset Delete"
msgstr "删除" msgstr "删除"
#: assets/templates/assets/asset_list.html:433 #: assets/templates/assets/asset_list.html:437
msgid "Asset Deleting failed." msgid "Asset Deleting failed."
msgstr "删除失败" msgstr "删除失败"
...@@ -2082,7 +2110,7 @@ msgstr "创建命令过滤器规则" ...@@ -2082,7 +2110,7 @@ msgstr "创建命令过滤器规则"
msgid "Update command filter rule" msgid "Update command filter rule"
msgstr "更新命令过滤器规则" msgstr "更新命令过滤器规则"
#: assets/views/domain.py:31 templates/_nav.html:23 #: assets/views/domain.py:31 templates/_nav.html:43
msgid "Domain list" msgid "Domain list"
msgstr "网域列表" msgstr "网域列表"
...@@ -2153,7 +2181,7 @@ msgstr "文件名" ...@@ -2153,7 +2181,7 @@ msgstr "文件名"
#: audits/templates/audits/ftp_log_list.html:79 #: audits/templates/audits/ftp_log_list.html:79
#: ops/templates/ops/command_execution_list.html:68 #: ops/templates/ops/command_execution_list.html:68
#: ops/templates/ops/task_list.html:31 #: ops/templates/ops/task_list.html:31
#: users/templates/users/user_detail.html:462 #: users/templates/users/user_detail.html:464
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:14
#: xpack/plugins/cloud/api.py:61 #: xpack/plugins/cloud/api.py:61
msgid "Success" msgid "Success"
...@@ -2191,8 +2219,8 @@ msgstr "启用" ...@@ -2191,8 +2219,8 @@ msgstr "启用"
msgid "-" msgid "-"
msgstr "" msgstr ""
#: audits/models.py:77 xpack/plugins/cloud/models.py:267 #: audits/models.py:77 xpack/plugins/cloud/models.py:264
#: xpack/plugins/cloud/models.py:290 #: xpack/plugins/cloud/models.py:287
msgid "Failed" msgid "Failed"
msgstr "失败" msgstr "失败"
...@@ -2214,21 +2242,21 @@ msgstr "Agent" ...@@ -2214,21 +2242,21 @@ msgstr "Agent"
#: audits/models.py:85 audits/templates/audits/login_log_list.html:62 #: audits/models.py:85 audits/templates/audits/login_log_list.html:62
#: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms.py:175 users/models/user.py:352 #: users/forms.py:175 users/models/user.py:395
#: users/templates/users/first_login.html:45 #: users/templates/users/first_login.html:45
msgid "MFA" msgid "MFA"
msgstr "MFA" msgstr "MFA"
#: audits/models.py:86 audits/templates/audits/login_log_list.html:63 #: audits/models.py:86 audits/templates/audits/login_log_list.html:63
#: xpack/plugins/change_auth_plan/models.py:417 #: xpack/plugins/change_auth_plan/models.py:416
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:15
#: xpack/plugins/cloud/models.py:281 #: xpack/plugins/cloud/models.py:278
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69
msgid "Reason" msgid "Reason"
msgstr "原因" msgstr "原因"
#: audits/models.py:87 audits/templates/audits/login_log_list.html:64 #: audits/models.py:87 audits/templates/audits/login_log_list.html:64
#: xpack/plugins/cloud/models.py:278 xpack/plugins/cloud/models.py:313 #: xpack/plugins/cloud/models.py:275 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:70
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:65
msgid "Status" msgid "Status"
...@@ -2246,10 +2274,11 @@ msgstr "登录日期" ...@@ -2246,10 +2274,11 @@ msgstr "登录日期"
#: perms/templates/perms/asset_permission_detail.html:86 #: perms/templates/perms/asset_permission_detail.html:86
#: perms/templates/perms/remote_app_permission_detail.html:78 #: perms/templates/perms/remote_app_permission_detail.html:78
#: terminal/models.py:165 terminal/templates/terminal/session_list.html:34 #: terminal/models.py:165 terminal/templates/terminal/session_list.html:34
#: xpack/plugins/change_auth_plan/models.py:250 #: xpack/plugins/change_auth_plan/models.py:249
#: xpack/plugins/change_auth_plan/models.py:420 #: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:59
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:17
#: xpack/plugins/gathered_user/models.py:143
msgid "Date start" msgid "Date start"
msgstr "开始日期" msgstr "开始日期"
...@@ -2297,47 +2326,46 @@ msgstr "城市" ...@@ -2297,47 +2326,46 @@ msgstr "城市"
msgid "Date" msgid "Date"
msgstr "日期" msgstr "日期"
#: audits/views.py:85 audits/views.py:129 audits/views.py:166 #: audits/views.py:86 audits/views.py:130 audits/views.py:167
#: audits/views.py:211 audits/views.py:243 templates/_nav.html:87 #: audits/views.py:212 audits/views.py:244 templates/_nav.html:129
#: templates/_nav_audits.html:22
msgid "Audits" msgid "Audits"
msgstr "日志审计" msgstr "日志审计"
#: audits/views.py:86 templates/_nav.html:91 templates/_nav_audits.html:26 #: audits/views.py:87 templates/_nav.html:133
msgid "FTP log" msgid "FTP log"
msgstr "FTP日志" msgstr "FTP日志"
#: audits/views.py:130 templates/_nav.html:92 templates/_nav_audits.html:27 #: audits/views.py:131 templates/_nav.html:134
msgid "Operate log" msgid "Operate log"
msgstr "操作日志" msgstr "操作日志"
#: audits/views.py:167 templates/_nav.html:93 templates/_nav_audits.html:28 #: audits/views.py:168 templates/_nav.html:135
msgid "Password change log" msgid "Password change log"
msgstr "改密日志" msgstr "改密日志"
#: audits/views.py:212 templates/_nav.html:90 templates/_nav_audits.html:25 #: audits/views.py:213 templates/_nav.html:132
msgid "Login log" msgid "Login log"
msgstr "登录日志" msgstr "登录日志"
#: audits/views.py:244 #: audits/views.py:245
msgid "Command execution log" msgid "Command execution log"
msgstr "命令执行" msgstr "命令执行"
#: authentication/api/auth.py:51 authentication/api/token.py:45 #: authentication/api/auth.py:61 authentication/api/token.py:45
#: authentication/templates/authentication/login.html:52 #: authentication/templates/authentication/login.html:52
#: authentication/templates/authentication/new_login.html:77 #: authentication/templates/authentication/new_login.html:77
msgid "Log in frequently and try again later" msgid "Log in frequently and try again later"
msgstr "登录频繁, 稍后重试" msgstr "登录频繁, 稍后重试"
#: authentication/api/auth.py:76 #: authentication/api/auth.py:86
msgid "Please carry seed value and conduct MFA secondary certification" msgid "Please carry seed value and conduct MFA secondary certification"
msgstr "请携带seed值, 进行MFA二次认证" msgstr "请携带seed值, 进行MFA二次认证"
#: authentication/api/auth.py:156 #: authentication/api/auth.py:176
msgid "Please verify the user name and password first" msgid "Please verify the user name and password first"
msgstr "请先进行用户名和密码验证" msgstr "请先进行用户名和密码验证"
#: authentication/api/auth.py:161 #: authentication/api/auth.py:181
msgid "MFA certification failed" msgid "MFA certification failed"
msgstr "MFA认证失败" msgstr "MFA认证失败"
...@@ -2463,18 +2491,19 @@ msgid "Secret" ...@@ -2463,18 +2491,19 @@ msgid "Secret"
msgstr "密文" msgstr "密文"
#: authentication/templates/authentication/_access_key_modal.html:48 #: authentication/templates/authentication/_access_key_modal.html:48
#: users/templates/users/_granted_assets.html:75
msgid "Show" msgid "Show"
msgstr "显示" msgstr "显示"
#: authentication/templates/authentication/_access_key_modal.html:66 #: authentication/templates/authentication/_access_key_modal.html:66
#: users/models/user.py:287 users/templates/users/user_profile.html:94 #: users/models/user.py:330 users/templates/users/user_profile.html:94
#: users/templates/users/user_profile.html:163 #: users/templates/users/user_profile.html:163
#: users/templates/users/user_profile.html:166 #: users/templates/users/user_profile.html:166
msgid "Disable" msgid "Disable"
msgstr "禁用" msgstr "禁用"
#: authentication/templates/authentication/_access_key_modal.html:67 #: authentication/templates/authentication/_access_key_modal.html:67
#: users/models/user.py:288 users/templates/users/user_profile.html:92 #: users/models/user.py:331 users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:170 #: users/templates/users/user_profile.html:170
msgid "Enable" msgid "Enable"
msgstr "启用" msgstr "启用"
...@@ -2611,8 +2640,8 @@ msgstr "欢迎回来,请输入用户名和密码登录" ...@@ -2611,8 +2640,8 @@ msgstr "欢迎回来,请输入用户名和密码登录"
msgid "Please enable cookies and try again." msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie" msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:174 users/views/user.py:386 #: authentication/views/login.py:174 users/views/user.py:393
#: users/views/user.py:411 #: users/views/user.py:418
msgid "MFA code invalid, or ntp sync server time" msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确,或者服务器端时间不对" msgstr "MFA验证码不正确,或者服务器端时间不对"
...@@ -2670,19 +2699,6 @@ msgstr "" ...@@ -2670,19 +2699,6 @@ msgstr ""
msgid "Encrypt field using Secret Key" msgid "Encrypt field using Secret Key"
msgstr "" msgstr ""
#: common/mixins/api.py:63
#, python-format
msgid "%(name)s was %(action)s successfully"
msgstr "%(name)s %(action)s成功"
#: common/mixins/api.py:64
msgid "create"
msgstr "创建"
#: common/mixins/api.py:64
msgid "update"
msgstr "更新"
#: common/mixins/models.py:31 #: common/mixins/models.py:31
msgid "is discard" msgid "is discard"
msgstr "" msgstr ""
...@@ -2699,11 +2715,11 @@ msgstr "不能包含特殊字符" ...@@ -2699,11 +2715,11 @@ msgstr "不能包含特殊字符"
msgid "This field must be unique." msgid "This field must be unique."
msgstr "字段必须唯一" msgstr "字段必须唯一"
#: jumpserver/views.py:188 templates/_nav.html:4 templates/_nav_audits.html:4 #: jumpserver/views.py:184 templates/_nav.html:7
msgid "Dashboard" msgid "Dashboard"
msgstr "仪表盘" msgstr "仪表盘"
#: jumpserver/views.py:197 #: jumpserver/views.py:193
msgid "" msgid ""
"<div>Luna is a separately deployed program, you need to deploy Luna, coco, " "<div>Luna is a separately deployed program, you need to deploy Luna, coco, "
"configure nginx for url distribution,</div> </div>If you see this page, " "configure nginx for url distribution,</div> </div>If you see this page, "
...@@ -2782,54 +2798,55 @@ msgstr "Become" ...@@ -2782,54 +2798,55 @@ msgstr "Become"
msgid "Create by" msgid "Create by"
msgstr "创建者" msgstr "创建者"
#: ops/models/adhoc.py:224 #: ops/models/adhoc.py:226
msgid "{} Start task: {}" msgid "{} Start task: {}"
msgstr "{} 任务开始: {}" msgstr "{} 任务开始: {}"
#: ops/models/adhoc.py:227 #: ops/models/adhoc.py:238
msgid "{} Task finish" msgid "{} Task finish"
msgstr "{} 任务结束" msgstr "{} 任务结束"
#: ops/models/adhoc.py:325 #: ops/models/adhoc.py:329
msgid "Start time" msgid "Start time"
msgstr "开始时间" msgstr "开始时间"
#: ops/models/adhoc.py:326 #: ops/models/adhoc.py:330
msgid "End time" msgid "End time"
msgstr "完成时间" msgstr "完成时间"
#: ops/models/adhoc.py:327 ops/templates/ops/adhoc_history.html:57 #: ops/models/adhoc.py:331 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:33 #: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:33
#: xpack/plugins/change_auth_plan/models.py:253 #: xpack/plugins/change_auth_plan/models.py:252
#: xpack/plugins/change_auth_plan/models.py:423 #: xpack/plugins/change_auth_plan/models.py:422
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
#: xpack/plugins/gathered_user/models.py:146
msgid "Time" msgid "Time"
msgstr "时间" msgstr "时间"
#: ops/models/adhoc.py:328 ops/templates/ops/adhoc_detail.html:106 #: ops/models/adhoc.py:332 ops/templates/ops/adhoc_detail.html:106
#: ops/templates/ops/adhoc_history.html:55 #: ops/templates/ops/adhoc_history.html:55
#: ops/templates/ops/adhoc_history_detail.html:69 #: ops/templates/ops/adhoc_history_detail.html:69
#: ops/templates/ops/task_detail.html:84 ops/templates/ops/task_history.html:61 #: ops/templates/ops/task_detail.html:84 ops/templates/ops/task_history.html:61
msgid "Is finished" msgid "Is finished"
msgstr "是否完成" msgstr "是否完成"
#: ops/models/adhoc.py:329 ops/templates/ops/adhoc_history.html:56 #: ops/models/adhoc.py:333 ops/templates/ops/adhoc_history.html:56
#: ops/templates/ops/task_history.html:62 #: ops/templates/ops/task_history.html:62
msgid "Is success" msgid "Is success"
msgstr "是否成功" msgstr "是否成功"
#: ops/models/adhoc.py:330 #: ops/models/adhoc.py:334
msgid "Adhoc raw result" msgid "Adhoc raw result"
msgstr "结果" msgstr "结果"
#: ops/models/adhoc.py:331 #: ops/models/adhoc.py:335
msgid "Adhoc result summary" msgid "Adhoc result summary"
msgstr "汇总" msgstr "汇总"
#: ops/models/command.py:22 #: ops/models/command.py:22
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:56 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:56
#: xpack/plugins/cloud/models.py:276 #: xpack/plugins/cloud/models.py:273
msgid "Result" msgid "Result"
msgstr "结果" msgstr "结果"
...@@ -2864,6 +2881,7 @@ msgstr "运行用户" ...@@ -2864,6 +2881,7 @@ msgstr "运行用户"
#: ops/templates/ops/adhoc_detail.html:94 ops/templates/ops/task_list.html:28 #: ops/templates/ops/adhoc_detail.html:94 ops/templates/ops/task_list.html:28
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:18 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:18
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:19
msgid "Run times" msgid "Run times"
msgstr "执行次数" msgstr "执行次数"
...@@ -2950,39 +2968,39 @@ msgstr "成功资产" ...@@ -2950,39 +2968,39 @@ msgstr "成功资产"
msgid "Task log" msgid "Task log"
msgstr "任务列表" msgstr "任务列表"
#: ops/templates/ops/command_execution_create.html:71 #: ops/templates/ops/command_execution_create.html:90
#: terminal/templates/terminal/session_detail.html:91 #: terminal/templates/terminal/session_detail.html:91
#: terminal/templates/terminal/session_detail.html:100 #: terminal/templates/terminal/session_detail.html:100
msgid "Go" msgid "Go"
msgstr "" msgstr ""
#: ops/templates/ops/command_execution_create.html:197 #: ops/templates/ops/command_execution_create.html:215
msgid "Selected assets" msgid "Selected assets"
msgstr "已选择资产" msgstr "已选择资产"
#: ops/templates/ops/command_execution_create.html:200 #: ops/templates/ops/command_execution_create.html:218
msgid "In total" msgid "In total"
msgstr "总共" msgstr "总共"
#: ops/templates/ops/command_execution_create.html:236 #: ops/templates/ops/command_execution_create.html:254
msgid "" msgid ""
"Select the left asset, select the running system user, execute command in " "Select the left asset, select the running system user, execute command in "
"batch" "batch"
msgstr "选择左侧资产, 选择运行的系统用户,批量执行命令" msgstr "选择左侧资产, 选择运行的系统用户,批量执行命令"
#: ops/templates/ops/command_execution_create.html:257 #: ops/templates/ops/command_execution_create.html:275
msgid "Unselected assets" msgid "Unselected assets"
msgstr "没有选中资产" msgstr "没有选中资产"
#: ops/templates/ops/command_execution_create.html:261 #: ops/templates/ops/command_execution_create.html:279
msgid "No input command" msgid "No input command"
msgstr "没有输入命令" msgstr "没有输入命令"
#: ops/templates/ops/command_execution_create.html:265 #: ops/templates/ops/command_execution_create.html:283
msgid "No system user was selected" msgid "No system user was selected"
msgstr "没有选择系统用户" msgstr "没有选择系统用户"
#: ops/templates/ops/command_execution_create.html:310 #: ops/templates/ops/command_execution_create.html:328
msgid "Pending" msgid "Pending"
msgstr "等待" msgstr "等待"
...@@ -3031,6 +3049,7 @@ msgstr "版本" ...@@ -3031,6 +3049,7 @@ msgstr "版本"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:54
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:141 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:141
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:55
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:49
msgid "Run" msgid "Run"
msgstr "执行" msgstr "执行"
...@@ -3044,11 +3063,12 @@ msgstr "更新任务内容: {}" ...@@ -3044,11 +3063,12 @@ msgstr "更新任务内容: {}"
#: ops/views/adhoc.py:45 ops/views/adhoc.py:71 ops/views/adhoc.py:85 #: ops/views/adhoc.py:45 ops/views/adhoc.py:71 ops/views/adhoc.py:85
#: ops/views/adhoc.py:99 ops/views/adhoc.py:113 ops/views/adhoc.py:127 #: ops/views/adhoc.py:99 ops/views/adhoc.py:113 ops/views/adhoc.py:127
#: ops/views/adhoc.py:141 ops/views/command.py:47 ops/views/command.py:77 #: ops/views/adhoc.py:141 ops/views/command.py:48 ops/views/command.py:79
msgid "Ops" msgid "Ops"
msgstr "作业中心" msgstr "作业中心"
#: ops/views/adhoc.py:46 templates/_nav.html:81 #: ops/views/adhoc.py:46 templates/_nav.html:115
#: xpack/plugins/gathered_user/views.py:35
msgid "Task list" msgid "Task list"
msgstr "任务列表" msgstr "任务列表"
...@@ -3056,15 +3076,15 @@ msgstr "任务列表" ...@@ -3056,15 +3076,15 @@ msgstr "任务列表"
msgid "Task run history" msgid "Task run history"
msgstr "执行历史" msgstr "执行历史"
#: ops/views/command.py:48 #: ops/views/command.py:49
msgid "Command execution list" msgid "Command execution list"
msgstr "命令执行列表" msgstr "命令执行列表"
#: ops/views/command.py:78 templates/_nav_user.html:26 #: ops/views/command.py:80 templates/_nav_user.html:26
msgid "Command execution" msgid "Command execution"
msgstr "命令执行" msgstr "命令执行"
#: orgs/mixins/models.py:61 orgs/mixins/serializers.py:25 orgs/models.py:29 #: orgs/mixins/models.py:61 orgs/mixins/serializers.py:26 orgs/models.py:30
msgid "Organization" msgid "Organization"
msgstr "组织" msgstr "组织"
...@@ -3081,11 +3101,11 @@ msgstr "空" ...@@ -3081,11 +3101,11 @@ msgstr "空"
#: perms/templates/perms/asset_permission_list.html:71 #: perms/templates/perms/asset_permission_list.html:71
#: perms/templates/perms/asset_permission_list.html:118 #: perms/templates/perms/asset_permission_list.html:118
#: perms/templates/perms/remote_app_permission_list.html:16 #: perms/templates/perms/remote_app_permission_list.html:16
#: templates/_nav.html:14 users/forms.py:286 users/models/group.py:26 #: templates/_nav.html:21 users/forms.py:286 users/models/group.py:26
#: users/models/user.py:336 users/templates/users/_select_user_modal.html:16 #: users/models/user.py:379 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:217 #: users/templates/users/user_detail.html:218
#: users/templates/users/user_list.html:38 #: users/templates/users/user_list.html:38
#: xpack/plugins/orgs/templates/orgs/org_list.html:15 #: xpack/plugins/orgs/templates/orgs/org_list.html:16
msgid "User group" msgid "User group"
msgstr "用户组" msgstr "用户组"
...@@ -3095,7 +3115,7 @@ msgid "" ...@@ -3095,7 +3115,7 @@ msgid ""
"downloading files" "downloading files"
msgstr "提示:RDP 协议不支持单独控制上传或下载文件" msgstr "提示:RDP 协议不支持单独控制上传或下载文件"
#: perms/forms/asset_permission.py:102 perms/forms/remote_app_permission.py:47 #: perms/forms/asset_permission.py:102 perms/forms/remote_app_permission.py:50
msgid "User or group at least one required" msgid "User or group at least one required"
msgstr "用户和用户组至少选一个" msgstr "用户和用户组至少选一个"
...@@ -3123,19 +3143,19 @@ msgstr "上传下载" ...@@ -3123,19 +3143,19 @@ msgstr "上传下载"
msgid "Actions" msgid "Actions"
msgstr "动作" msgstr "动作"
#: perms/models/asset_permission.py:85 templates/_nav.html:44 #: perms/models/asset_permission.py:85 templates/_nav.html:72
msgid "Asset permission" msgid "Asset permission"
msgstr "资产授权" msgstr "资产授权"
#: perms/models/base.py:53 #: perms/models/base.py:53
#: perms/templates/perms/asset_permission_detail.html:90 #: perms/templates/perms/asset_permission_detail.html:90
#: perms/templates/perms/remote_app_permission_detail.html:82 #: perms/templates/perms/remote_app_permission_detail.html:82
#: users/models/user.py:368 users/templates/users/user_detail.html:107 #: users/models/user.py:411 users/templates/users/user_detail.html:107
#: users/templates/users/user_profile.html:120 #: users/templates/users/user_profile.html:120
msgid "Date expired" msgid "Date expired"
msgstr "失效日期" msgstr "失效日期"
#: perms/models/remote_app_permission.py:19 #: perms/models/remote_app_permission.py:20
msgid "RemoteApp permission" msgid "RemoteApp permission"
msgstr "远程应用授权" msgstr "远程应用授权"
...@@ -3165,13 +3185,14 @@ msgstr "添加资产" ...@@ -3165,13 +3185,14 @@ msgstr "添加资产"
#: perms/templates/perms/asset_permission_detail.html:157 #: perms/templates/perms/asset_permission_detail.html:157
#: perms/templates/perms/asset_permission_user.html:97 #: perms/templates/perms/asset_permission_user.html:97
#: perms/templates/perms/asset_permission_user.html:125 #: perms/templates/perms/asset_permission_user.html:125
#: perms/templates/perms/remote_app_permission_detail.html:148
#: perms/templates/perms/remote_app_permission_remote_app.html:96 #: perms/templates/perms/remote_app_permission_remote_app.html:96
#: perms/templates/perms/remote_app_permission_user.html:96 #: perms/templates/perms/remote_app_permission_user.html:96
#: perms/templates/perms/remote_app_permission_user.html:124 #: perms/templates/perms/remote_app_permission_user.html:124
#: settings/templates/settings/terminal_setting.html:98 #: settings/templates/settings/terminal_setting.html:98
#: settings/templates/settings/terminal_setting.html:120 #: settings/templates/settings/terminal_setting.html:120
#: users/templates/users/user_group_detail.html:95 #: users/templates/users/user_group_detail.html:95
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:90 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:80
#: xpack/plugins/orgs/templates/orgs/org_detail.html:93 #: xpack/plugins/orgs/templates/orgs/org_detail.html:93
#: xpack/plugins/orgs/templates/orgs/org_detail.html:130 #: xpack/plugins/orgs/templates/orgs/org_detail.html:130
msgid "Add" msgid "Add"
...@@ -3182,13 +3203,13 @@ msgid "Add node to this permission" ...@@ -3182,13 +3203,13 @@ msgid "Add node to this permission"
msgstr "添加节点" msgstr "添加节点"
#: perms/templates/perms/asset_permission_asset.html:109 #: perms/templates/perms/asset_permission_asset.html:109
#: users/templates/users/user_detail.html:234 #: users/templates/users/user_detail.html:235
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:115 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:105
msgid "Join" msgid "Join"
msgstr "加入" msgstr "加入"
#: perms/templates/perms/asset_permission_create_update.html:61 #: perms/templates/perms/asset_permission_create_update.html:61
#: perms/templates/perms/remote_app_permission_create_update.html:60 #: perms/templates/perms/remote_app_permission_create_update.html:61
msgid "Validity period" msgid "Validity period"
msgstr "有效期" msgstr "有效期"
...@@ -3217,6 +3238,7 @@ msgid "System user count" ...@@ -3217,6 +3238,7 @@ msgid "System user count"
msgstr "系统用户数量" msgstr "系统用户数量"
#: perms/templates/perms/asset_permission_detail.html:148 #: perms/templates/perms/asset_permission_detail.html:148
#: perms/templates/perms/remote_app_permission_detail.html:139
msgid "Select system users" msgid "Select system users"
msgstr "选择系统用户" msgstr "选择系统用户"
...@@ -3286,14 +3308,14 @@ msgstr "添加用户组" ...@@ -3286,14 +3308,14 @@ msgstr "添加用户组"
#: perms/views/asset_permission.py:33 perms/views/asset_permission.py:65 #: perms/views/asset_permission.py:33 perms/views/asset_permission.py:65
#: perms/views/asset_permission.py:82 perms/views/asset_permission.py:99 #: perms/views/asset_permission.py:82 perms/views/asset_permission.py:99
#: perms/views/asset_permission.py:136 perms/views/asset_permission.py:171 #: perms/views/asset_permission.py:139 perms/views/asset_permission.py:170
#: perms/views/remote_app_permission.py:33 #: perms/views/remote_app_permission.py:33
#: perms/views/remote_app_permission.py:49 #: perms/views/remote_app_permission.py:49
#: perms/views/remote_app_permission.py:66 #: perms/views/remote_app_permission.py:66
#: perms/views/remote_app_permission.py:81 #: perms/views/remote_app_permission.py:81
#: perms/views/remote_app_permission.py:108 #: perms/views/remote_app_permission.py:115
#: perms/views/remote_app_permission.py:145 templates/_nav.html:41 #: perms/views/remote_app_permission.py:148 templates/_nav.html:69
#: xpack/plugins/orgs/templates/orgs/org_list.html:21 #: xpack/plugins/orgs/templates/orgs/org_list.html:22
msgid "Perms" msgid "Perms"
msgstr "权限管理" msgstr "权限管理"
...@@ -3313,11 +3335,11 @@ msgstr "更新资产授权" ...@@ -3313,11 +3335,11 @@ msgstr "更新资产授权"
msgid "Asset permission detail" msgid "Asset permission detail"
msgstr "资产授权详情" msgstr "资产授权详情"
#: perms/views/asset_permission.py:137 #: perms/views/asset_permission.py:140
msgid "Asset permission user list" msgid "Asset permission user list"
msgstr "资产授权用户列表" msgstr "资产授权用户列表"
#: perms/views/asset_permission.py:172 #: perms/views/asset_permission.py:171
msgid "Asset permission asset list" msgid "Asset permission asset list"
msgstr "资产授权资产列表" msgstr "资产授权资产列表"
...@@ -3337,11 +3359,11 @@ msgstr "更新远程应用授权规则" ...@@ -3337,11 +3359,11 @@ msgstr "更新远程应用授权规则"
msgid "RemoteApp permission detail" msgid "RemoteApp permission detail"
msgstr "远程应用授权详情" msgstr "远程应用授权详情"
#: perms/views/remote_app_permission.py:109 #: perms/views/remote_app_permission.py:116
msgid "RemoteApp permission user list" msgid "RemoteApp permission user list"
msgstr "远程应用授权用户列表" msgstr "远程应用授权用户列表"
#: perms/views/remote_app_permission.py:146 #: perms/views/remote_app_permission.py:149
msgid "RemoteApp permission RemoteApp list" msgid "RemoteApp permission RemoteApp list"
msgstr "远程应用授权远程应用列表" msgstr "远程应用授权远程应用列表"
...@@ -3357,21 +3379,21 @@ msgstr "连接LDAP成功" ...@@ -3357,21 +3379,21 @@ msgstr "连接LDAP成功"
msgid "Match {} s users" msgid "Match {} s users"
msgstr "匹配 {} 个用户" msgstr "匹配 {} 个用户"
#: settings/api.py:159 #: settings/api.py:161
msgid "succeed: {} failed: {} total: {}" msgid "succeed: {} failed: {} total: {}"
msgstr "成功:{} 失败:{} 总数:{}" msgstr "成功:{} 失败:{} 总数:{}"
#: settings/api.py:181 settings/api.py:217 #: settings/api.py:183 settings/api.py:219
msgid "" msgid ""
"Error: Account invalid (Please make sure the information such as Access key " "Error: Account invalid (Please make sure the information such as Access key "
"or Secret key is correct)" "or Secret key is correct)"
msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)" msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)"
#: settings/api.py:187 settings/api.py:223 #: settings/api.py:189 settings/api.py:225
msgid "Create succeed" msgid "Create succeed"
msgstr "创建成功" msgstr "创建成功"
#: settings/api.py:205 settings/api.py:243 #: settings/api.py:207 settings/api.py:245
#: settings/templates/settings/terminal_setting.html:154 #: settings/templates/settings/terminal_setting.html:154
msgid "Delete succeed" msgid "Delete succeed"
msgstr "删除成功" msgstr "删除成功"
...@@ -3684,7 +3706,7 @@ msgid "Please submit the LDAP configuration before import" ...@@ -3684,7 +3706,7 @@ msgid "Please submit the LDAP configuration before import"
msgstr "请先提交LDAP配置再进行导入" msgstr "请先提交LDAP配置再进行导入"
#: settings/templates/settings/_ldap_list_users_modal.html:32 #: settings/templates/settings/_ldap_list_users_modal.html:32
#: users/models/user.py:332 users/templates/users/user_detail.html:71 #: users/models/user.py:375 users/templates/users/user_detail.html:71
#: users/templates/users/user_profile.html:59 #: users/templates/users/user_profile.html:59
msgid "Email" msgid "Email"
msgstr "邮件" msgstr "邮件"
...@@ -3830,7 +3852,7 @@ msgid "Endpoint suffix" ...@@ -3830,7 +3852,7 @@ msgid "Endpoint suffix"
msgstr "端点后缀" msgstr "端点后缀"
#: settings/templates/settings/replay_storage_create.html:136 #: settings/templates/settings/replay_storage_create.html:136
#: xpack/plugins/cloud/models.py:307 #: xpack/plugins/cloud/models.py:304
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:109 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:109
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:62 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:62
msgid "Region" msgid "Region"
...@@ -3888,7 +3910,7 @@ msgstr "用户来源不是LDAP" ...@@ -3888,7 +3910,7 @@ msgstr "用户来源不是LDAP"
#: settings/views.py:19 settings/views.py:46 settings/views.py:73 #: settings/views.py:19 settings/views.py:46 settings/views.py:73
#: settings/views.py:103 settings/views.py:131 settings/views.py:144 #: settings/views.py:103 settings/views.py:131 settings/views.py:144
#: settings/views.py:158 settings/views.py:185 templates/_nav.html:122 #: settings/views.py:158 settings/views.py:185 templates/_nav.html:170
msgid "Settings" msgid "Settings"
msgstr "系统设置" msgstr "系统设置"
...@@ -3917,14 +3939,15 @@ msgstr "文档" ...@@ -3917,14 +3939,15 @@ msgstr "文档"
msgid "Commercial support" msgid "Commercial support"
msgstr "商业支持" msgstr "商业支持"
#: templates/_header_bar.html:70 templates/_nav_user.html:32 users/forms.py:154 #: templates/_header_bar.html:70 templates/_nav.html:30
#: templates/_nav_user.html:32 users/forms.py:154
#: users/templates/users/_user.html:43 #: users/templates/users/_user.html:43
#: users/templates/users/first_login.html:39 #: users/templates/users/first_login.html:39
#: users/templates/users/user_password_update.html:40 #: users/templates/users/user_password_update.html:40
#: users/templates/users/user_profile.html:17 #: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37 #: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:61 #: users/templates/users/user_profile_update.html:61
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:224 #: users/templates/users/user_pubkey_update.html:37 users/views/user.py:231
msgid "Profile" msgid "Profile"
msgstr "个人信息" msgstr "个人信息"
...@@ -4029,72 +4052,74 @@ msgstr "" ...@@ -4029,72 +4052,74 @@ msgstr ""
"\"%(user_pubkey_update)s\"> 链接 </a> 更新\n" "\"%(user_pubkey_update)s\"> 链接 </a> 更新\n"
" " " "
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:45 #: templates/_nav.html:17 users/views/group.py:28 users/views/group.py:45
#: users/views/group.py:63 users/views/group.py:81 users/views/group.py:98 #: users/views/group.py:63 users/views/group.py:82 users/views/group.py:99
#: users/views/login.py:154 users/views/user.py:60 users/views/user.py:77 #: users/views/login.py:154 users/views/user.py:61 users/views/user.py:78
#: users/views/user.py:121 users/views/user.py:188 users/views/user.py:210 #: users/views/user.py:122 users/views/user.py:189 users/views/user.py:217
#: users/views/user.py:263 users/views/user.py:298 #: users/views/user.py:270 users/views/user.py:305
msgid "Users" msgid "Users"
msgstr "用户管理" msgstr "用户管理"
#: templates/_nav.html:13 users/views/user.py:61 #: templates/_nav.html:20 users/views/user.py:62
msgid "User list" msgid "User list"
msgstr "用户列表" msgstr "用户列表"
#: templates/_nav.html:27 #: templates/_nav.html:47
msgid "Command filters" msgid "Command filters"
msgstr "命令过滤" msgstr "命令过滤"
#: templates/_nav.html:55 templates/_nav_audits.html:11 #: templates/_nav.html:88 terminal/views/command.py:21
#: terminal/views/command.py:21 terminal/views/session.py:43 #: terminal/views/session.py:43 terminal/views/session.py:54
#: terminal/views/session.py:54 terminal/views/session.py:78 #: terminal/views/session.py:78 terminal/views/terminal.py:32
#: terminal/views/terminal.py:32 terminal/views/terminal.py:48 #: terminal/views/terminal.py:48 terminal/views/terminal.py:61
#: terminal/views/terminal.py:61
msgid "Sessions" msgid "Sessions"
msgstr "会话管理" msgstr "会话管理"
#: templates/_nav.html:58 templates/_nav_audits.html:14 #: templates/_nav.html:91
msgid "Session online" msgid "Session online"
msgstr "在线会话" msgstr "在线会话"
#: templates/_nav.html:59 templates/_nav_audits.html:15 #: templates/_nav.html:92 terminal/views/session.py:55
#: terminal/views/session.py:55
msgid "Session offline" msgid "Session offline"
msgstr "历史会话" msgstr "历史会话"
#: templates/_nav.html:60 templates/_nav_audits.html:16 #: templates/_nav.html:93
msgid "Commands" msgid "Commands"
msgstr "命令记录" msgstr "命令记录"
#: templates/_nav.html:63 templates/_nav_user.html:37 #: templates/_nav.html:96 templates/_nav_user.html:37
msgid "Web terminal" msgid "Web terminal"
msgstr "Web终端" msgstr "Web终端"
#: templates/_nav.html:68 templates/_nav_user.html:42 #: templates/_nav.html:97 templates/_nav_user.html:42
msgid "File manager" msgid "File manager"
msgstr "文件管理" msgstr "文件管理"
#: templates/_nav.html:72 #: templates/_nav.html:101
msgid "Terminal" msgid "Terminal"
msgstr "终端管理" msgstr "终端管理"
#: templates/_nav.html:78 #: templates/_nav.html:112
msgid "Job Center" msgid "Job Center"
msgstr "作业中心" msgstr "作业中心"
#: templates/_nav.html:82 templates/_nav.html:94 templates/_nav_audits.html:29 #: templates/_nav.html:116 templates/_nav.html:136
msgid "Batch command" msgid "Batch command"
msgstr "批量命令" msgstr "批量命令"
#: templates/_nav.html:100 #: templates/_nav.html:118
msgid "Task monitor"
msgstr "任务监控"
#: templates/_nav.html:146
msgid "XPack" msgid "XPack"
msgstr "" msgstr ""
#: templates/_nav.html:108 xpack/plugins/cloud/views.py:28 #: templates/_nav.html:154 xpack/plugins/cloud/views.py:28
msgid "Account list" msgid "Account list"
msgstr "账户列表" msgstr "账户列表"
#: templates/_nav.html:109 #: templates/_nav.html:155
msgid "Sync instance" msgid "Sync instance"
msgstr "同步实例" msgstr "同步实例"
...@@ -4476,11 +4501,11 @@ msgid "" ...@@ -4476,11 +4501,11 @@ msgid ""
"You should use your ssh client tools connect terminal: {} <br /> <br />{}" "You should use your ssh client tools connect terminal: {} <br /> <br />{}"
msgstr "你可以使用ssh客户端工具连接终端" msgstr "你可以使用ssh客户端工具连接终端"
#: users/api/user.py:187 #: users/api/user.py:173
msgid "Could not reset self otp, use profile reset instead" msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置" msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
#: users/forms.py:33 users/models/user.py:340 #: users/forms.py:33 users/models/user.py:383
#: users/templates/users/_select_user_modal.html:15 #: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87 #: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:37 #: users/templates/users/user_list.html:37
...@@ -4501,7 +4526,7 @@ msgstr "" ...@@ -4501,7 +4526,7 @@ msgstr ""
msgid "Paste user id_rsa.pub here." msgid "Paste user id_rsa.pub here."
msgstr "复制用户公钥到这里" msgstr "复制用户公钥到这里"
#: users/forms.py:52 users/templates/users/user_detail.html:225 #: users/forms.py:52 users/templates/users/user_detail.html:226
msgid "Join user groups" msgid "Join user groups"
msgstr "添加到用户组" msgstr "添加到用户组"
...@@ -4509,11 +4534,11 @@ msgstr "添加到用户组" ...@@ -4509,11 +4534,11 @@ msgstr "添加到用户组"
msgid "Public key should not be the same as your old one." msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同" msgstr "不能和原来的密钥相同"
#: users/forms.py:91 users/forms.py:252 users/serializers/v1.py:94 #: users/forms.py:91 users/forms.py:252 users/serializers/v1.py:116
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法" msgstr "ssh密钥不合法"
#: users/forms.py:104 users/views/login.py:114 users/views/user.py:280 #: users/forms.py:104 users/views/login.py:114 users/views/user.py:287
msgid "* Your password does not meet the requirements" msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求" msgstr "* 您的密码不符合要求"
...@@ -4525,7 +4550,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户" ...@@ -4525,7 +4550,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户"
msgid "Set password" msgid "Set password"
msgstr "设置密码" msgstr "设置密码"
#: users/forms.py:133 xpack/plugins/change_auth_plan/models.py:86 #: users/forms.py:133 xpack/plugins/change_auth_plan/models.py:88
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
...@@ -4599,7 +4624,7 @@ msgstr "选择用户" ...@@ -4599,7 +4624,7 @@ msgstr "选择用户"
msgid "User auth from {}, go there change password" msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码" msgstr "用户认证源来自 {}, 请去相应系统修改密码"
#: users/models/user.py:126 users/models/user.py:465 #: users/models/user.py:126 users/models/user.py:508
msgid "Administrator" msgid "Administrator"
msgstr "管理员" msgstr "管理员"
...@@ -4607,72 +4632,85 @@ msgstr "管理员" ...@@ -4607,72 +4632,85 @@ msgstr "管理员"
msgid "Application" msgid "Application"
msgstr "应用程序" msgstr "应用程序"
#: users/models/user.py:129 #: users/models/user.py:129 xpack/plugins/orgs/forms.py:38
#: xpack/plugins/orgs/templates/orgs/org_list.html:14
msgid "Auditor" msgid "Auditor"
msgstr "审计员" msgstr "审计员"
#: users/models/user.py:289 users/templates/users/user_profile.html:90 #: users/models/user.py:139
msgid "Org admin"
msgstr "组织管理员"
#: users/models/user.py:141
msgid "Org auditor"
msgstr "组织审计员"
#: users/models/user.py:332 users/templates/users/user_profile.html:90
msgid "Force enable" msgid "Force enable"
msgstr "强制启用" msgstr "强制启用"
#: users/models/user.py:343 #: users/models/user.py:386
msgid "Avatar" msgid "Avatar"
msgstr "头像" msgstr "头像"
#: users/models/user.py:346 users/templates/users/user_detail.html:82 #: users/models/user.py:389 users/templates/users/user_detail.html:82
msgid "Wechat" msgid "Wechat"
msgstr "微信" msgstr "微信"
#: users/models/user.py:375 users/templates/users/user_detail.html:103 #: users/models/user.py:418 users/templates/users/user_detail.html:103
#: users/templates/users/user_list.html:39 #: users/templates/users/user_list.html:39
#: users/templates/users/user_profile.html:102 #: users/templates/users/user_profile.html:102
msgid "Source" msgid "Source"
msgstr "用户来源" msgstr "用户来源"
#: users/models/user.py:379 #: users/models/user.py:422
msgid "Date password last updated" msgid "Date password last updated"
msgstr "最后更新密码日期" msgstr "最后更新密码日期"
#: users/models/user.py:468 #: users/models/user.py:511
msgid "Administrator is the super user of system" msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员" msgstr "Administrator是初始的超级管理员"
#: users/serializers/v1.py:39 #: users/serializers/v1.py:45
msgid "Groups name" msgid "Groups name"
msgstr "用户组名" msgstr "用户组名"
#: users/serializers/v1.py:40 #: users/serializers/v1.py:46
msgid "Source name" msgid "Source name"
msgstr "用户来源名" msgstr "用户来源名"
#: users/serializers/v1.py:41 #: users/serializers/v1.py:47
msgid "Is first login" msgid "Is first login"
msgstr "首次登录" msgstr "首次登录"
#: users/serializers/v1.py:42 #: users/serializers/v1.py:48
msgid "Role name" msgid "Role name"
msgstr "角色名" msgstr "角色名"
#: users/serializers/v1.py:43 #: users/serializers/v1.py:49
msgid "Is valid" msgid "Is valid"
msgstr "账户是否有效" msgstr "账户是否有效"
#: users/serializers/v1.py:44 #: users/serializers/v1.py:50
msgid "Is expired" msgid "Is expired"
msgstr " 是否过期" msgstr " 是否过期"
#: users/serializers/v1.py:45 #: users/serializers/v1.py:51
msgid "Avatar url" msgid "Avatar url"
msgstr "头像路径" msgstr "头像路径"
#: users/serializers/v1.py:54 #: users/serializers/v1.py:72
msgid "Role limit to {}" msgid "Role limit to {}"
msgstr "角色只能为 {}" msgstr "角色只能为 {}"
#: users/serializers/v1.py:66 #: users/serializers/v1.py:84
msgid "Password does not match security rules" msgid "Password does not match security rules"
msgstr "密码不满足安全规则" msgstr "密码不满足安全规则"
#: users/serializers/v1.py:147
msgid "Auditors cannot be join in the group"
msgstr ""
#: users/serializers_v2/user.py:36 #: users/serializers_v2/user.py:36
msgid "name not unique" msgid "name not unique"
msgstr "名称重复" msgstr "名称重复"
...@@ -4723,7 +4761,7 @@ msgid "Import users" ...@@ -4723,7 +4761,7 @@ msgid "Import users"
msgstr "导入用户" msgstr "导入用户"
#: users/templates/users/_user_update_modal.html:4 #: users/templates/users/_user_update_modal.html:4
#: users/templates/users/user_update.html:4 users/views/user.py:122 #: users/templates/users/user_update.html:4 users/views/user.py:123
msgid "Update user" msgid "Update user"
msgstr "更新用户" msgstr "更新用户"
...@@ -4801,7 +4839,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry" ...@@ -4801,7 +4839,7 @@ msgid "Always young, always with tears in my eyes. Stay foolish Stay hungry"
msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry" msgstr "永远年轻,永远热泪盈眶 stay foolish stay hungry"
#: users/templates/users/reset_password.html:46 #: users/templates/users/reset_password.html:46
#: users/templates/users/user_detail.html:377 users/utils.py:84 #: users/templates/users/user_detail.html:379 users/utils.py:84
msgid "Reset password" msgid "Reset password"
msgstr "重置密码" msgstr "重置密码"
...@@ -4866,12 +4904,12 @@ msgid "Very strong" ...@@ -4866,12 +4904,12 @@ msgid "Very strong"
msgstr "很强" msgstr "很强"
#: users/templates/users/user_create.html:4 #: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:28 users/views/user.py:78 #: users/templates/users/user_list.html:28 users/views/user.py:79
msgid "Create user" msgid "Create user"
msgstr "创建用户" msgstr "创建用户"
#: users/templates/users/user_detail.html:19 #: users/templates/users/user_detail.html:19
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:189 #: users/templates/users/user_granted_asset.html:18 users/views/user.py:190
msgid "User detail" msgid "User detail"
msgstr "用户详情" msgstr "用户详情"
...@@ -4918,7 +4956,7 @@ msgid "Send reset ssh key mail" ...@@ -4918,7 +4956,7 @@ msgid "Send reset ssh key mail"
msgstr "发送重置密钥邮件" msgstr "发送重置密钥邮件"
#: users/templates/users/user_detail.html:203 #: users/templates/users/user_detail.html:203
#: users/templates/users/user_detail.html:465 #: users/templates/users/user_detail.html:467
msgid "Unblock user" msgid "Unblock user"
msgstr "解除登录限制" msgstr "解除登录限制"
...@@ -4926,52 +4964,52 @@ msgstr "解除登录限制" ...@@ -4926,52 +4964,52 @@ msgstr "解除登录限制"
msgid "Unblock" msgid "Unblock"
msgstr "解除" msgstr "解除"
#: users/templates/users/user_detail.html:320 #: users/templates/users/user_detail.html:322
msgid "Goto profile page enable MFA" msgid "Goto profile page enable MFA"
msgstr "请去个人信息页面启用自己的MFA" msgstr "请去个人信息页面启用自己的MFA"
#: users/templates/users/user_detail.html:376 #: users/templates/users/user_detail.html:378
msgid "An e-mail has been sent to the user`s mailbox." msgid "An e-mail has been sent to the user`s mailbox."
msgstr "已发送邮件到用户邮箱" msgstr "已发送邮件到用户邮箱"
#: users/templates/users/user_detail.html:387 #: users/templates/users/user_detail.html:389
msgid "This will reset the user password and send a reset mail" msgid "This will reset the user password and send a reset mail"
msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱"
#: users/templates/users/user_detail.html:402 #: users/templates/users/user_detail.html:404
msgid "" msgid ""
"The reset-ssh-public-key E-mail has been sent successfully. Please inform " "The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"the user to update his new ssh public key." "the user to update his new ssh public key."
msgstr "重设密钥邮件将会发送到用户邮箱" msgstr "重设密钥邮件将会发送到用户邮箱"
#: users/templates/users/user_detail.html:403 #: users/templates/users/user_detail.html:405
msgid "Reset SSH public key" msgid "Reset SSH public key"
msgstr "重置SSH密钥" msgstr "重置SSH密钥"
#: users/templates/users/user_detail.html:413 #: users/templates/users/user_detail.html:415
msgid "This will reset the user public key and send a reset mail" msgid "This will reset the user public key and send a reset mail"
msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱"
#: users/templates/users/user_detail.html:431 #: users/templates/users/user_detail.html:433
msgid "Successfully updated the SSH public key." msgid "Successfully updated the SSH public key."
msgstr "更新ssh密钥成功" msgstr "更新ssh密钥成功"
#: users/templates/users/user_detail.html:432 #: users/templates/users/user_detail.html:434
#: users/templates/users/user_detail.html:436 #: users/templates/users/user_detail.html:438
msgid "User SSH public key update" msgid "User SSH public key update"
msgstr "ssh密钥" msgstr "ssh密钥"
#: users/templates/users/user_detail.html:481 #: users/templates/users/user_detail.html:483
msgid "After unlocking the user, the user can log in normally." msgid "After unlocking the user, the user can log in normally."
msgstr "解除用户登录限制后,此用户即可正常登录" msgstr "解除用户登录限制后,此用户即可正常登录"
#: users/templates/users/user_detail.html:495 #: users/templates/users/user_detail.html:497
msgid "Reset user MFA success" msgid "Reset user MFA success"
msgstr "重置用户MFA成功" msgstr "重置用户MFA成功"
#: users/templates/users/user_group_detail.html:22 #: users/templates/users/user_group_detail.html:22
#: users/templates/users/user_group_granted_asset.html:18 #: users/templates/users/user_group_granted_asset.html:18
#: users/views/group.py:82 #: users/views/group.py:83
msgid "User group detail" msgid "User group detail"
msgstr "用户组详情" msgstr "用户组详情"
...@@ -5005,24 +5043,24 @@ msgstr "用户组删除失败" ...@@ -5005,24 +5043,24 @@ msgstr "用户组删除失败"
msgid "This will delete the selected users !!!" msgid "This will delete the selected users !!!"
msgstr "删除选中用户 !!!" msgstr "删除选中用户 !!!"
#: users/templates/users/user_list.html:267 #: users/templates/users/user_list.html:262
msgid "User Deleted." msgid "User Deleted."
msgstr "已被删除" msgstr "已被删除"
#: users/templates/users/user_list.html:268 #: users/templates/users/user_list.html:263
#: users/templates/users/user_list.html:272 #: users/templates/users/user_list.html:267
msgid "User Delete" msgid "User Delete"
msgstr "删除" msgstr "删除"
#: users/templates/users/user_list.html:271 #: users/templates/users/user_list.html:266
msgid "User Deleting failed." msgid "User Deleting failed."
msgstr "用户删除失败" msgstr "用户删除失败"
#: users/templates/users/user_list.html:324 #: users/templates/users/user_list.html:327
msgid "User is expired" msgid "User is expired"
msgstr "用户已失效" msgstr "用户已失效"
#: users/templates/users/user_list.html:327 #: users/templates/users/user_list.html:330
msgid "User is inactive" msgid "User is inactive"
msgstr "用户已禁用" msgstr "用户已禁用"
...@@ -5327,7 +5365,7 @@ msgstr "" ...@@ -5327,7 +5365,7 @@ msgstr ""
msgid "User group list" msgid "User group list"
msgstr "用户组列表" msgstr "用户组列表"
#: users/views/group.py:99 #: users/views/group.py:100
msgid "User group granted asset" msgid "User group granted asset"
msgstr "用户组授权资产" msgstr "用户组授权资产"
...@@ -5364,47 +5402,47 @@ msgstr "密码不一致" ...@@ -5364,47 +5402,47 @@ msgstr "密码不一致"
msgid "First login" msgid "First login"
msgstr "首次登录" msgstr "首次登录"
#: users/views/user.py:140 #: users/views/user.py:141
msgid "Bulk update user success" msgid "Bulk update user success"
msgstr "批量更新用户成功" msgstr "批量更新用户成功"
#: users/views/user.py:168 #: users/views/user.py:169
msgid "Bulk update user" msgid "Bulk update user"
msgstr "批量更新用户" msgstr "批量更新用户"
#: users/views/user.py:211 #: users/views/user.py:218
msgid "User granted assets" msgid "User granted assets"
msgstr "用户授权资产" msgstr "用户授权资产"
#: users/views/user.py:244 #: users/views/user.py:251
msgid "Profile setting" msgid "Profile setting"
msgstr "个人信息设置" msgstr "个人信息设置"
#: users/views/user.py:264 #: users/views/user.py:271
msgid "Password update" msgid "Password update"
msgstr "密码更新" msgstr "密码更新"
#: users/views/user.py:299 #: users/views/user.py:306
msgid "Public key update" msgid "Public key update"
msgstr "密钥更新" msgstr "密钥更新"
#: users/views/user.py:341 #: users/views/user.py:348
msgid "Password invalid" msgid "Password invalid"
msgstr "用户名或密码无效" msgstr "用户名或密码无效"
#: users/views/user.py:441 #: users/views/user.py:448
msgid "MFA enable success" msgid "MFA enable success"
msgstr "MFA 绑定成功" msgstr "MFA 绑定成功"
#: users/views/user.py:442 #: users/views/user.py:449
msgid "MFA enable success, return login page" msgid "MFA enable success, return login page"
msgstr "MFA 绑定成功,返回到登录页面" msgstr "MFA 绑定成功,返回到登录页面"
#: users/views/user.py:444 #: users/views/user.py:451
msgid "MFA disable success" msgid "MFA disable success"
msgstr "MFA 解绑成功" msgstr "MFA 解绑成功"
#: users/views/user.py:445 #: users/views/user.py:452
msgid "MFA disable success, return login page" msgid "MFA disable success, return login page"
msgstr "MFA 解绑成功,返回登录页面" msgstr "MFA 解绑成功,返回登录页面"
...@@ -5412,21 +5450,7 @@ msgstr "MFA 解绑成功,返回登录页面" ...@@ -5412,21 +5450,7 @@ msgstr "MFA 解绑成功,返回登录页面"
msgid "Password length" msgid "Password length"
msgstr "密码长度" msgstr "密码长度"
#: xpack/plugins/change_auth_plan/forms.py:51 #: xpack/plugins/change_auth_plan/forms.py:66
#: xpack/plugins/change_auth_plan/models.py:213
msgid "* For security, do not change {} user's password"
msgstr "* 为了安全,禁止更改 {} 用户的密码"
#: xpack/plugins/change_auth_plan/forms.py:61
msgid "* Please enter custom password"
msgstr "* 请输入自定义密码"
#: xpack/plugins/change_auth_plan/forms.py:70
#: xpack/plugins/cloud/serializers.py:73
msgid "* Please enter a valid crontab expression"
msgstr "* 请输入有效的 crontab 表达式"
#: xpack/plugins/change_auth_plan/forms.py:123
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:60 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:60
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:81 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:81
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:17 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:17
...@@ -5434,10 +5458,13 @@ msgstr "* 请输入有效的 crontab 表达式" ...@@ -5434,10 +5458,13 @@ msgstr "* 请输入有效的 crontab 表达式"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:41 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:41
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:72 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:72
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:16
#: xpack/plugins/gathered_user/forms.py:13
#: xpack/plugins/gathered_user/forms.py:41
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:32
msgid "Periodic perform" msgid "Periodic perform"
msgstr "定时执行" msgstr "定时执行"
#: xpack/plugins/change_auth_plan/forms.py:127 #: xpack/plugins/change_auth_plan/forms.py:70
msgid "" msgid ""
"Tips: The username of the user on the asset to be modified. if the user " "Tips: The username of the user on the asset to be modified. if the user "
"exists, change the password; If the user does not exist, create the user." "exists, change the password; If the user does not exist, create the user."
...@@ -5445,11 +5472,13 @@ msgstr "" ...@@ -5445,11 +5472,13 @@ msgstr ""
"提示:用户名为将要修改的资产上的用户的用户名。如果用户存在,则修改密码;如果" "提示:用户名为将要修改的资产上的用户的用户名。如果用户存在,则修改密码;如果"
"用户不存在,则创建用户。" "用户不存在,则创建用户。"
#: xpack/plugins/change_auth_plan/forms.py:131 xpack/plugins/cloud/forms.py:90 #: xpack/plugins/change_auth_plan/forms.py:74 xpack/plugins/cloud/forms.py:90
#: xpack/plugins/gathered_user/forms.py:44
msgid "Tips: (Units: hour)" msgid "Tips: (Units: hour)"
msgstr "提示:(单位: 时)" msgstr "提示:(单位: 时)"
#: xpack/plugins/change_auth_plan/forms.py:132 xpack/plugins/cloud/forms.py:91 #: xpack/plugins/change_auth_plan/forms.py:75 xpack/plugins/cloud/forms.py:91
#: xpack/plugins/gathered_user/forms.py:45
msgid "" msgid ""
"eg: Every Sunday 03:05 run <5 3 * * 0> <br> Tips: Using 5 digits linux " "eg: Every Sunday 03:05 run <5 3 * * 0> <br> Tips: Using 5 digits linux "
"crontab expressions <min hour day month week> (<a href='https://tool.lu/" "crontab expressions <min hour day month week> (<a href='https://tool.lu/"
...@@ -5461,88 +5490,117 @@ msgstr "" ...@@ -5461,88 +5490,117 @@ msgstr ""
"具</a>) <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行" "具</a>) <br>注意: 如果同时设置了定期执行和周期执行,优先使用定期执行"
#: xpack/plugins/change_auth_plan/meta.py:9 #: xpack/plugins/change_auth_plan/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:114 #: xpack/plugins/change_auth_plan/models.py:116
#: xpack/plugins/change_auth_plan/models.py:257 #: xpack/plugins/change_auth_plan/models.py:256
#: xpack/plugins/change_auth_plan/views.py:33 #: xpack/plugins/change_auth_plan/views.py:33
#: xpack/plugins/change_auth_plan/views.py:50 #: xpack/plugins/change_auth_plan/views.py:50
#: xpack/plugins/change_auth_plan/views.py:72 #: xpack/plugins/change_auth_plan/views.py:74
#: xpack/plugins/change_auth_plan/views.py:87 #: xpack/plugins/change_auth_plan/views.py:90
#: xpack/plugins/change_auth_plan/views.py:114 #: xpack/plugins/change_auth_plan/views.py:117
#: xpack/plugins/change_auth_plan/views.py:129 #: xpack/plugins/change_auth_plan/views.py:132
#: xpack/plugins/change_auth_plan/views.py:144 #: xpack/plugins/change_auth_plan/views.py:147
msgid "Change auth plan" msgid "Change auth plan"
msgstr "改密计划" msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:55 #: xpack/plugins/change_auth_plan/models.py:57
msgid "Custom password" msgid "Custom password"
msgstr "自定义密码" msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models.py:56 #: xpack/plugins/change_auth_plan/models.py:58
msgid "All assets use the same random password" msgid "All assets use the same random password"
msgstr "所有资产使用相同的随机密码" msgstr "所有资产使用相同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:57 #: xpack/plugins/change_auth_plan/models.py:59
msgid "All assets use different random password" msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码" msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:76 #: xpack/plugins/change_auth_plan/models.py:78
#: xpack/plugins/change_auth_plan/models.py:145 #: xpack/plugins/change_auth_plan/models.py:147
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100
#: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219 #: xpack/plugins/cloud/models.py:165 xpack/plugins/cloud/models.py:219
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:91
#: xpack/plugins/gathered_user/models.py:35
#: xpack/plugins/gathered_user/models.py:72
msgid "Cycle perform" msgid "Cycle perform"
msgstr "周期执行" msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:81 #: xpack/plugins/change_auth_plan/models.py:83
#: xpack/plugins/change_auth_plan/models.py:143 #: xpack/plugins/change_auth_plan/models.py:145
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92
#: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217 #: xpack/plugins/cloud/models.py:170 xpack/plugins/cloud/models.py:217
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
#: xpack/plugins/gathered_user/models.py:40
#: xpack/plugins/gathered_user/models.py:70
msgid "Regularly perform" msgid "Regularly perform"
msgstr "定期执行" msgstr "定期执行"
#: xpack/plugins/change_auth_plan/models.py:90 #: xpack/plugins/change_auth_plan/models.py:92
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:74
msgid "Password rules" msgid "Password rules"
msgstr "密码规则" msgstr "密码规则"
#: xpack/plugins/change_auth_plan/models.py:217 #: xpack/plugins/change_auth_plan/models.py:212
msgid "* For security, do not change {} user's password"
msgstr "* 为了安全,禁止更改 {} 用户的密码"
#: xpack/plugins/change_auth_plan/models.py:216
msgid "Assets is empty, please add the asset" msgid "Assets is empty, please add the asset"
msgstr "资产为空,请添加资产" msgstr "资产为空,请添加资产"
#: xpack/plugins/change_auth_plan/models.py:261 #: xpack/plugins/change_auth_plan/models.py:260
msgid "Change auth plan snapshot" msgid "Change auth plan snapshot"
msgstr "改密计划快照" msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:276 #: xpack/plugins/change_auth_plan/models.py:275
#: xpack/plugins/change_auth_plan/models.py:427 #: xpack/plugins/change_auth_plan/models.py:426
msgid "Change auth plan execution" msgid "Change auth plan execution"
msgstr "改密计划执行" msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:436 #: xpack/plugins/change_auth_plan/models.py:435
msgid "Change auth plan execution subtask" msgid "Change auth plan execution subtask"
msgstr "改密计划执行子任务" msgstr "改密计划执行子任务"
#: xpack/plugins/change_auth_plan/models.py:454 #: xpack/plugins/change_auth_plan/models.py:453
msgid "Authentication failed" msgid "Authentication failed"
msgstr "认证失败" msgstr "认证失败"
#: xpack/plugins/change_auth_plan/models.py:456 #: xpack/plugins/change_auth_plan/models.py:455
msgid "Connection timeout" msgid "Connection timeout"
msgstr "连接超时" msgstr "连接超时"
#: xpack/plugins/change_auth_plan/serializers.py:58
msgid "* For security, do not change {}'s password"
msgstr "* 为了安全,禁止更改 {} 的密码"
#: xpack/plugins/change_auth_plan/serializers.py:68
msgid "* Please enter custom password"
msgstr "* 请输入自定义密码"
#: xpack/plugins/change_auth_plan/serializers.py:78
msgid "* Please enter the correct password length"
msgstr "* 请输入正确的密码长度"
#: xpack/plugins/change_auth_plan/serializers.py:81
msgid "* Password length range 6-30 bits"
msgstr "* 密码长度范围 6-30 位"
#: xpack/plugins/change_auth_plan/serializers.py:97
#: xpack/plugins/cloud/serializers.py:73
msgid "* Please enter a valid crontab expression"
msgstr "* 请输入有效的 crontab 表达式"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:23 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:23
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:26 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:26
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:23 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:23
#: xpack/plugins/change_auth_plan/views.py:130 #: xpack/plugins/change_auth_plan/views.py:133
msgid "Plan execution list" msgid "Plan execution list"
msgstr "执行列表" msgstr "执行列表"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:76 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:66
msgid "Add asset to this plan" msgid "Add asset to this plan"
msgstr "添加资产" msgstr "添加资产"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:101 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:91
msgid "Add node to this plan" msgid "Add node to this plan"
msgstr "添加节点" msgstr "添加节点"
...@@ -5572,6 +5630,7 @@ msgstr "手动执行计划" ...@@ -5572,6 +5630,7 @@ msgstr "手动执行计划"
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:179 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:179
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:103 #: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:103
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:96
msgid "Execute failed" msgid "Execute failed"
msgstr "执行失败" msgstr "执行失败"
...@@ -5601,11 +5660,11 @@ msgstr "创建计划" ...@@ -5601,11 +5660,11 @@ msgstr "创建计划"
msgid "Plan list" msgid "Plan list"
msgstr "计划列表" msgstr "计划列表"
#: xpack/plugins/change_auth_plan/views.py:73 #: xpack/plugins/change_auth_plan/views.py:75
msgid "Update plan" msgid "Update plan"
msgstr "更新计划" msgstr "更新计划"
#: xpack/plugins/change_auth_plan/views.py:145 #: xpack/plugins/change_auth_plan/views.py:148
msgid "Plan execution task list" msgid "Plan execution task list"
msgstr "执行任务列表" msgstr "执行任务列表"
...@@ -5691,37 +5750,37 @@ msgstr "实例" ...@@ -5691,37 +5750,37 @@ msgstr "实例"
msgid "Date last sync" msgid "Date last sync"
msgstr "最后同步日期" msgstr "最后同步日期"
#: xpack/plugins/cloud/models.py:187 xpack/plugins/cloud/models.py:274 #: xpack/plugins/cloud/models.py:187 xpack/plugins/cloud/models.py:271
msgid "Sync instance task" msgid "Sync instance task"
msgstr "同步实例任务" msgstr "同步实例任务"
#: xpack/plugins/cloud/models.py:268 xpack/plugins/cloud/models.py:291 #: xpack/plugins/cloud/models.py:265 xpack/plugins/cloud/models.py:288
msgid "Succeed" msgid "Succeed"
msgstr "成功" msgstr "成功"
#: xpack/plugins/cloud/models.py:269 #: xpack/plugins/cloud/models.py:266
msgid "Partial succeed" msgid "Partial succeed"
msgstr "" msgstr ""
#: xpack/plugins/cloud/models.py:284 xpack/plugins/cloud/models.py:316 #: xpack/plugins/cloud/models.py:281 xpack/plugins/cloud/models.py:313
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:71 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:71
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:66 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:66
msgid "Date sync" msgid "Date sync"
msgstr "同步日期" msgstr "同步日期"
#: xpack/plugins/cloud/models.py:292 #: xpack/plugins/cloud/models.py:289
msgid "Exist" msgid "Exist"
msgstr "存在" msgstr "存在"
#: xpack/plugins/cloud/models.py:297 #: xpack/plugins/cloud/models.py:294
msgid "Sync task" msgid "Sync task"
msgstr "同步任务" msgstr "同步任务"
#: xpack/plugins/cloud/models.py:301 #: xpack/plugins/cloud/models.py:298
msgid "Sync instance task history" msgid "Sync instance task history"
msgstr "同步实例任务历史" msgstr "同步实例任务历史"
#: xpack/plugins/cloud/models.py:304 #: xpack/plugins/cloud/models.py:301
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:117 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:117
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:61 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:61
msgid "Instance" msgid "Instance"
...@@ -5841,6 +5900,53 @@ msgstr "创建同步实例任务" ...@@ -5841,6 +5900,53 @@ msgstr "创建同步实例任务"
msgid "Update sync Instance task" msgid "Update sync Instance task"
msgstr "更新同步实例任务" msgstr "更新同步实例任务"
#: xpack/plugins/gathered_user/meta.py:11
#: xpack/plugins/gathered_user/views.py:21
#: xpack/plugins/gathered_user/views.py:34
msgid "Gathered user"
msgstr "收集用户"
#: xpack/plugins/gathered_user/models.py:33
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:18
msgid "Periodic"
msgstr "定时执行"
#: xpack/plugins/gathered_user/models.py:57
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:48
msgid "Gather user task"
msgstr "收集用户任务"
#: xpack/plugins/gathered_user/models.py:140
msgid "Task"
msgstr "任务"
#: xpack/plugins/gathered_user/models.py:152
msgid "gather user task execution"
msgstr "收集用户执行"
#: xpack/plugins/gathered_user/models.py:158
msgid "Assets is empty, please change nodes"
msgstr "资产为空,请更改节点"
#: xpack/plugins/gathered_user/templates/gathered_user/task_list.html:7
#: xpack/plugins/gathered_user/views.py:50
msgid "Create task"
msgstr "创建任务"
#: xpack/plugins/gathered_user/views.py:22
msgid "Gathered user list"
msgstr "收集用户列表"
#: xpack/plugins/gathered_user/views.py:49
#: xpack/plugins/gathered_user/views.py:66 xpack/plugins/vault/meta.py:11
#: xpack/plugins/vault/views.py:23 xpack/plugins/vault/views.py:38
msgid "Vault"
msgstr "密码匣子"
#: xpack/plugins/gathered_user/views.py:67
msgid "Update task"
msgstr "更新任务"
#: xpack/plugins/interface/forms.py:17 xpack/plugins/interface/models.py:15 #: xpack/plugins/interface/forms.py:17 xpack/plugins/interface/models.py:15
msgid "Title of login page" msgid "Title of login page"
msgstr "登录页面标题" msgstr "登录页面标题"
...@@ -6020,6 +6126,12 @@ msgstr "无效的许可证" ...@@ -6020,6 +6126,12 @@ msgstr "无效的许可证"
msgid "Admin" msgid "Admin"
msgstr "管理员" msgstr "管理员"
#: xpack/plugins/orgs/forms.py:42
#, fuzzy
#| msgid "Select admins"
msgid "Select auditor"
msgstr "选择管理员"
#: xpack/plugins/orgs/meta.py:8 xpack/plugins/orgs/views.py:26 #: xpack/plugins/orgs/meta.py:8 xpack/plugins/orgs/views.py:26
#: xpack/plugins/orgs/views.py:43 xpack/plugins/orgs/views.py:60 #: xpack/plugins/orgs/views.py:43 xpack/plugins/orgs/views.py:60
#: xpack/plugins/orgs/views.py:77 #: xpack/plugins/orgs/views.py:77
...@@ -6051,11 +6163,6 @@ msgstr "创建组织" ...@@ -6051,11 +6163,6 @@ msgstr "创建组织"
msgid "Update org" msgid "Update org"
msgstr "更新组织" msgstr "更新组织"
#: xpack/plugins/vault/meta.py:11 xpack/plugins/vault/views.py:23
#: xpack/plugins/vault/views.py:38
msgid "Vault"
msgstr "密码匣子"
#: xpack/plugins/vault/templates/vault/_xpack_import_modal.html:4 #: xpack/plugins/vault/templates/vault/_xpack_import_modal.html:4
msgid "Import vault" msgid "Import vault"
msgstr "导入密码" msgstr "导入密码"
...@@ -6068,14 +6175,20 @@ msgstr "密码匣子" ...@@ -6068,14 +6175,20 @@ msgstr "密码匣子"
msgid "vault create" msgid "vault create"
msgstr "创建" msgstr "创建"
#~ msgid "* For security, do not change {}'s password" #~ msgid "Gather user plan"
#~ msgstr "* 为了安全,禁止更改 {} 的密码" #~ msgstr "收集用户计划"
#~ msgid "Task update"
#~ msgstr "更新"
#~ msgid "Task create"
#~ msgstr "创建"
#~ msgid "* Please enter the correct password length" #~ msgid "%(hostname)s was %(action)s successfully"
#~ msgstr "* 请输入正确的密码长度" #~ msgstr "%(hostname)s %(action)s成功"
#~ msgid "* Password length range 6-30 bits" #~ msgid "%(name)s was %(action)s successfully"
#~ msgstr "* 密码长度范围 6-30 位" #~ msgstr "%(name)s %(action)s成功"
#~ msgid "Loading..." #~ msgid "Loading..."
#~ msgstr "加载中..." #~ msgstr "加载中..."
......
...@@ -6,7 +6,9 @@ import os ...@@ -6,7 +6,9 @@ import os
from django.conf import settings from django.conf import settings
from django.utils.timezone import get_current_timezone from django.utils.timezone import get_current_timezone
from django.db.utils import ProgrammingError, OperationalError from django.db.utils import ProgrammingError, OperationalError
from django_celery_beat.models import PeriodicTask, IntervalSchedule, CrontabSchedule from django_celery_beat.models import (
PeriodicTask, IntervalSchedule, CrontabSchedule, PeriodicTasks
)
def create_or_update_celery_periodic_tasks(tasks): def create_or_update_celery_periodic_tasks(tasks):
...@@ -75,17 +77,20 @@ def create_or_update_celery_periodic_tasks(tasks): ...@@ -75,17 +77,20 @@ def create_or_update_celery_periodic_tasks(tasks):
task = PeriodicTask.objects.update_or_create( task = PeriodicTask.objects.update_or_create(
defaults=defaults, name=name, defaults=defaults, name=name,
) )
PeriodicTasks.update_changed()
return task return task
def disable_celery_periodic_task(task_name): def disable_celery_periodic_task(task_name):
from django_celery_beat.models import PeriodicTask from django_celery_beat.models import PeriodicTask
PeriodicTask.objects.filter(name=task_name).update(enabled=False) PeriodicTask.objects.filter(name=task_name).update(enabled=False)
PeriodicTasks.update_changed()
def delete_celery_periodic_task(task_name): def delete_celery_periodic_task(task_name):
from django_celery_beat.models import PeriodicTask from django_celery_beat.models import PeriodicTask
PeriodicTask.objects.filter(name=task_name).delete() PeriodicTask.objects.filter(name=task_name).delete()
PeriodicTasks.update_changed()
def get_celery_task_log_path(task_id): def get_celery_task_log_path(task_id):
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.conf import settings
from .ansible.inventory import BaseInventory from .ansible.inventory import BaseInventory
from common.utils import get_logger from common.utils import get_logger
...@@ -14,6 +15,7 @@ logger = get_logger(__file__) ...@@ -14,6 +15,7 @@ logger = get_logger(__file__)
class JMSBaseInventory(BaseInventory): class JMSBaseInventory(BaseInventory):
windows_ssh_default_ssh = settings.WINDOWS_SSH_DEFAULT_SHELL
def convert_to_ansible(self, asset, run_as_admin=False): def convert_to_ansible(self, asset, run_as_admin=False):
info = { info = {
...@@ -33,7 +35,7 @@ class JMSBaseInventory(BaseInventory): ...@@ -33,7 +35,7 @@ class JMSBaseInventory(BaseInventory):
if asset.is_windows(): if asset.is_windows():
info["vars"].update({ info["vars"].update({
"ansible_connection": "ssh", "ansible_connection": "ssh",
"ansible_shell_type": "cmd", "ansible_shell_type": self.windows_ssh_default_ssh,
}) })
for label in asset.labels.all(): for label in asset.labels.all():
info["vars"].update({ info["vars"].update({
...@@ -49,7 +51,7 @@ class JMSBaseInventory(BaseInventory): ...@@ -49,7 +51,7 @@ class JMSBaseInventory(BaseInventory):
def make_proxy_command(asset): def make_proxy_command(asset):
gateway = asset.domain.random_gateway() gateway = asset.domain.random_gateway()
proxy_command_list = [ proxy_command_list = [
"ssh", "-p", str(gateway.port), "ssh", "-o", "Port={}".format(gateway.port),
"-o", "StrictHostKeyChecking=no", "-o", "StrictHostKeyChecking=no",
"{}@{}".format(gateway.username, gateway.ip), "{}@{}".format(gateway.username, gateway.ip),
"-W", "%h:%p", "-q", "-W", "%h:%p", "-q",
......
# Generated by Django 2.1.7 on 2019-09-19 13:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ops', '0007_auto_20190724_2002'),
]
operations = [
migrations.AddField(
model_name='task',
name='date_updated',
field=models.DateTimeField(auto_now=True, verbose_name='Date updated'),
),
migrations.AlterField(
model_name='task',
name='date_created',
field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Date created'),
),
migrations.AlterModelOptions(
name='task',
options={'get_latest_by': 'date_created',
'ordering': ('-date_updated',)},
),
]
...@@ -13,7 +13,7 @@ from django.utils import timezone ...@@ -13,7 +13,7 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django_celery_beat.models import PeriodicTask from django_celery_beat.models import PeriodicTask
from common.utils import get_signer, get_logger from common.utils import get_signer, get_logger, lazyproperty
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from ..celery.utils import delete_celery_periodic_task, \ from ..celery.utils import delete_celery_periodic_task, \
create_or_update_celery_periodic_tasks, \ create_or_update_celery_periodic_tasks, \
...@@ -42,7 +42,8 @@ class Task(models.Model): ...@@ -42,7 +42,8 @@ class Task(models.Model):
is_deleted = models.BooleanField(default=False) is_deleted = models.BooleanField(default=False)
comment = models.TextField(blank=True, verbose_name=_("Comment")) comment = models.TextField(blank=True, verbose_name=_("Comment"))
created_by = models.CharField(max_length=128, blank=True, default='') created_by = models.CharField(max_length=128, blank=True, default='')
date_created = models.DateTimeField(auto_now_add=True, db_index=True) date_created = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_("Date created"))
date_updated = models.DateTimeField(auto_now=True, verbose_name=_("Date updated"))
__latest_adhoc = None __latest_adhoc = None
_ignore_auto_created_by = True _ignore_auto_created_by = True
...@@ -51,16 +52,39 @@ class Task(models.Model): ...@@ -51,16 +52,39 @@ class Task(models.Model):
return str(self.id).split('-')[-1] return str(self.id).split('-')[-1]
@property @property
def latest_adhoc(self): def versions(self):
if not self.__latest_adhoc: return self.adhoc.all().count()
self.__latest_adhoc = self.get_latest_adhoc()
return self.__latest_adhoc @property
def is_success(self):
if self.latest_history:
return self.latest_history.is_success
else:
return False
@latest_adhoc.setter @property
def latest_adhoc(self, item): def timedelta(self):
self.__latest_adhoc = item if self.latest_history:
return self.latest_history.timedelta
else:
return 0
@property @property
def date_start(self):
if self.latest_history:
return self.latest_history.date_start
else:
return None
@property
def assets_amount(self):
return self.latest_adhoc.hosts.count()
@lazyproperty
def latest_adhoc(self):
return self.get_latest_adhoc()
@lazyproperty
def latest_history(self): def latest_history(self):
try: try:
return self.history.all().latest() return self.history.all().latest()
...@@ -139,6 +163,7 @@ class Task(models.Model): ...@@ -139,6 +163,7 @@ class Task(models.Model):
class Meta: class Meta:
db_table = 'ops_task' db_table = 'ops_task'
unique_together = ('name', 'created_by') unique_together = ('name', 'created_by')
ordering = ('-date_updated',)
get_latest_by = 'date_created' get_latest_by = 'date_created'
...@@ -218,30 +243,35 @@ class AdHoc(models.Model): ...@@ -218,30 +243,35 @@ class AdHoc(models.Model):
hid = str(uuid.uuid4()) hid = str(uuid.uuid4())
history = AdHocRunHistory(id=hid, adhoc=self, task=self.task) history = AdHocRunHistory(id=hid, adhoc=self, task=self.task)
time_start = time.time() time_start = time.time()
date_start = timezone.now()
is_success = False
try: try:
date_start = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') date_start_s = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
history.date_start = timezone.now() print(_("{} Start task: {}").format(date_start_s, self.task.name))
print(_("{} Start task: {}").format(date_start, self.task.name))
raw, summary = self._run_only() raw, summary = self._run_only()
date_end = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') is_success = summary.get('success', False)
print(_("{} Task finish").format(date_end))
history.is_finished = True
if summary.get('dark'):
history.is_success = False
else:
history.is_success = True
history.result = raw
history.summary = summary
return raw, summary return raw, summary
except Exception as e: except Exception as e:
logger.error(e, exc_info=True) logger.error(e, exc_info=True)
return {}, {"dark": {"all": str(e)}, "contacted": []} summary = {}
raw = {"dark": {"all": str(e)}, "contacted": []}
return raw, summary
finally: finally:
history.date_finished = timezone.now() date_end = timezone.now()
history.timedelta = time.time() - time_start date_end_s = date_end.strftime('%Y-%m-%d %H:%M:%S')
history.save() print(_("{} Task finish").format(date_end_s))
print('.\n\n.')
AdHocRunHistory.objects.filter(id=history.id).update(
date_start=date_start,
is_finished=True,
is_success=is_success,
date_finished=timezone.now(),
timedelta=time.time() - time_start
)
def _run_only(self): def _run_only(self):
Task.objects.filter(id=self.task.id).update(date_updated=timezone.now())
runner = AdHocRunner(self.inventory, options=self.options) runner = AdHocRunner(self.inventory, options=self.options)
try: try:
result = runner.run( result = runner.run(
......
...@@ -19,7 +19,12 @@ class CeleryTaskSerializer(serializers.Serializer): ...@@ -19,7 +19,12 @@ class CeleryTaskSerializer(serializers.Serializer):
class TaskSerializer(serializers.ModelSerializer): class TaskSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Task model = Task
fields = '__all__' fields = [
'id', 'name', 'interval', 'crontab', 'is_periodic',
'is_deleted', 'comment', 'created_by', 'date_created',
'versions', 'is_success', 'timedelta', 'assets_amount',
'date_updated', 'history_summary',
]
class AdHocSerializer(serializers.ModelSerializer): class AdHocSerializer(serializers.ModelSerializer):
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import os import os
import subprocess import subprocess
import datetime import datetime
import time
from django.conf import settings from django.conf import settings
from celery import shared_task, subtask from celery import shared_task, subtask
...@@ -122,3 +123,22 @@ def hello123(): ...@@ -122,3 +123,22 @@ def hello123():
def hello_callback(result): def hello_callback(result):
print(result) print(result)
print("Hello callback") print("Hello callback")
@shared_task
def add(a, b):
time.sleep(5)
return a + b
@shared_task
def add_m(x):
from celery import chain
a = range(x)
b = [a[i:i + 10] for i in range(0, len(a), 10)]
s = list()
s.append(add.s(b[0], b[1]))
for i in b[1:]:
s.append(add.s(i))
res = chain(*tuple(s))()
return res
{% extends '_base_list.html' %} {% extends '_base_list.html' %}
{% load i18n %} {% load i18n static %}
{% load static %} {% block table_search %}{% endblock %}
{% block table_container %}
{% block content_left_head %} <table class="table table-striped table-bordered table-hover " id="task_list_table">
{# <div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create task" %} </a></div>#} <thead>
{% endblock %} <tr>
<th class="text-center">
<input id="" type="checkbox" class="ipt_check_all">
{% block table_search %} </th>
<form id="search_form" method="get" action="" class="pull-right form-inline"> <th class="text-left">{% trans 'Name' %}</th>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="{% trans 'Search' %}" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
{% trans "Search" %}
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th class="text-center"></th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Run times' %}</th> <th class="text-center">{% trans 'Run times' %}</th>
<th class="text-center">{% trans 'Versions' %}</th> <th class="text-center">{% trans 'Versions' %}</th>
<th class="text-center">{% trans 'Hosts' %}</th> <th class="text-center">{% trans 'Hosts' %}</th>
...@@ -32,70 +16,72 @@ ...@@ -32,70 +16,72 @@
<th class="text-center">{% trans 'Date' %}</th> <th class="text-center">{% trans 'Date' %}</th>
<th class="text-center">{% trans 'Time' %}</th> <th class="text-center">{% trans 'Time' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
{% endblock %}
{% block table_body %}
{% for object in task_list %}
<tr class="gradeX">
<td class="text-center"><input type="checkbox" class="cbx-term"> </td>
<td class="text-left"><a href="{% url 'ops:task-detail' pk=object.id %}">{{ object.name }}</a></td>
<td class="text-center">
<span class="text-danger">{{ object.history_summary.failed }}</span>/<span class="text-navy">{{ object.history_summary.success}}</span>/{{ object.history_summary.total}}
</td>
<td class="text-center">{{ object.adhoc.all | length}}</td>
<td class="text-center">{{ object.latest_adhoc.hosts | length}}</td>
<td class="text-center">
{% if object.latest_history %}
{% if object.latest_history.is_success %}
<i class="fa fa-check text-navy"></i>
{% else %}
<i class="fa fa-times text-danger"></i>
{% endif %}
{% endif %}
</td>
<td class="text-center">{{ object.latest_history.date_start }}</td>
<td class="text-center">
{% if object.latest_history %}
{{ object.latest_history.timedelta|floatformat }} s
{% endif %}
</td>
<td class="text-center">
<a data-uid="{{ object.id }}" class="btn btn-xs btn-primary btn-run">{% trans "Run" %}</a>
<a data-uid="{{ object.id }}" class="btn btn-xs btn-danger btn-del">{% trans "Delete" %}</a>
</td>
</tr> </tr>
{% endfor %} </thead>
</table>
{% endblock %} {% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function () {
$('table').DataTable({ var options = {
"searching": false, ele: $('#task_list_table'),
"paging": false, buttons: [],
"bInfo" : false, columnDefs: [
"order": [], {targets: 1, createdCell: function (td, cellData, rowData) {
"columnDefs": [ cellData = htmlEscape(cellData);
{ "targets": 0, "orderable": false }, var innerHtml = '<a href="{% url "ops:task-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'
{ "targets": 4, "orderable": false }, innerHtml = innerHtml.replace('{{ DEFAULT_PK }}', rowData.id);
{ "targets": 5, "orderable": false }, $(td).html(innerHtml);
{ "targets": 8, "orderable": false } }},
] {targets: 2, createdCell: function (td, cellData) {
}); var innerHtml = '<span class="text-danger">failed</span>/<span class="text-navy">success</span>/total';
$('.select2').select2({ if (cellData) {
dropdownAutoWidth : true, innerHtml = innerHtml.replace('failed', cellData.failed)
width: 'auto' .replace('success', cellData.success)
}); .replace('total', cellData.total);
$('#date .input-daterange').datepicker({ $(td).html(innerHtml);
format: "yyyy-mm-dd", } else {
todayBtn: "linked", $(td).html('')
keyboardNavigation: false, }
forceParse: false, }},
calendarWeeks: true, {targets: 5, createdCell: function (td, cellData) {
autoclose: true var successBtn = '<i class="fa fa-check text-navy"></i>';
}); var failedBtn = '<i class="fa fa-times text-danger"></i>';
if (cellData) {
$(td).html(successBtn)
} else {
$(td).html(failedBtn)
}
}},
{targets: 6, createdCell: function (td, cellData) {
$(td).html(toSafeLocalDateStr(cellData));
}},
{targets: 7, createdCell: function (td, cellData) {
var delta = readableSecond(cellData);
$(td).html(delta);
}},
{
targets: 8,
createdCell: function (td, cellData, rowData) {
var runBtn = '<a data-uid="ID" class="btn btn-xs btn-primary btn-run">{% trans "Run" %}</a> '.replace('ID', cellData);
var delBtn = '<a data-uid="ID" class="btn btn-xs btn-danger btn-del">{% trans "Delete" %}</a>'.replace('ID', cellData);
$(td).html(runBtn + delBtn)
}
}
],
ajax_url: '{% url "api-ops:task-list" %}',
columns: [
{data: "id"}, {data: "name", className: "text-left"}, {data: "history_summary", orderable: false},
{data: "versions", orderable: false}, {data: "assets_amount", orderable: false},
{data: "is_success", orderable: false}, {data: "date_updated"},
{data: "timedelta", orderable:false}, {data: "id", orderable: false},
],
order: [],
op_html: $('#actions').html()
};
jumpserver.initServerSideDataTable(options);
}).on('click', '.btn-del', function () { }).on('click', '.btn-del', function () {
var $this = $(this); var $this = $(this);
var name = $this.closest("tr").find(":nth-child(2)").children('a').html(); var name = $this.closest("tr").find(":nth-child(2)").children('a').html();
...@@ -122,7 +108,6 @@ $(document).ready(function() { ...@@ -122,7 +108,6 @@ $(document).ready(function() {
success: success, success: success,
success_message: "{% trans 'Task start: ' %}" + " " + name success_message: "{% trans 'Task start: ' %}" + " " + name
}); });
}) })
</script> </script>
{% endblock %} {% endblock %}
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from django.views.generic import ListView, DetailView from django.views.generic import ListView, DetailView, TemplateView
from common.mixins import DatetimeSearchMixin from common.mixins import DatetimeSearchMixin
from common.permissions import PermissionsMixin, IsOrgAdmin from common.permissions import PermissionsMixin, IsOrgAdmin
...@@ -17,7 +17,7 @@ __all__ = [ ...@@ -17,7 +17,7 @@ __all__ = [
] ]
class TaskListView(PermissionsMixin, DatetimeSearchMixin, ListView): class TaskListView(PermissionsMixin, TemplateView):
paginate_by = settings.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
model = Task model = Task
ordering = ('-date_created',) ordering = ('-date_created',)
...@@ -26,27 +26,10 @@ class TaskListView(PermissionsMixin, DatetimeSearchMixin, ListView): ...@@ -26,27 +26,10 @@ class TaskListView(PermissionsMixin, DatetimeSearchMixin, ListView):
keyword = '' keyword = ''
permission_classes = [IsOrgAdmin] permission_classes = [IsOrgAdmin]
def get_queryset(self):
queryset = super().get_queryset()
if current_org.is_real():
queryset = queryset.filter(created_by=current_org.id)
else:
queryset = queryset.filter(created_by='')
self.keyword = self.request.GET.get('keyword', '')
if self.keyword:
queryset = queryset.filter(
name__icontains=self.keyword,
)
return queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Ops'), 'app': _('Ops'),
'action': _('Task list'), 'action': _('Task list'),
'date_from': self.date_from,
'date_to': self.date_to,
'keyword': self.keyword,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from common.mixins import IDInCacheFilterMixin from common.mixins import CommonApiMixin
from ..utils import set_to_root_org from ..utils import set_to_root_org
from ..models import Organization from ..models import Organization
...@@ -20,20 +20,23 @@ class RootOrgViewMixin: ...@@ -20,20 +20,23 @@ class RootOrgViewMixin:
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
class OrgModelViewSet(IDInCacheFilterMixin, ModelViewSet): class OrgQuerySetMixin:
def get_queryset(self):
return super().get_queryset().all()
class OrgBulkModelViewSet(IDInCacheFilterMixin, BulkModelViewSet):
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset().all() queryset = super().get_queryset().all()
if hasattr(self, 'swagger_fake_view'):
return queryset[:1]
if hasattr(self, 'action') and self.action == 'list' and \ if hasattr(self, 'action') and self.action == 'list' and \
hasattr(self, 'serializer_class') and \ hasattr(self, 'serializer_class') and \
hasattr(self.serializer_class, 'setup_eager_loading'): hasattr(self.serializer_class, 'setup_eager_loading'):
queryset = self.serializer_class.setup_eager_loading(queryset) queryset = self.serializer_class.setup_eager_loading(queryset)
return queryset return queryset
class OrgModelViewSet(CommonApiMixin, OrgQuerySetMixin, ModelViewSet):
pass
class OrgBulkModelViewSet(CommonApiMixin, OrgQuerySetMixin, BulkModelViewSet):
def allow_bulk_destroy(self, qs, filtered): def allow_bulk_destroy(self, qs, filtered):
if qs.count() <= filtered.count(): if qs.count() <= filtered.count():
return False return False
......
...@@ -11,7 +11,8 @@ from ..utils import get_current_org_id_for_serializer ...@@ -11,7 +11,8 @@ from ..utils import get_current_org_id_for_serializer
__all__ = [ __all__ = [
"OrgResourceSerializerMixin", "BulkOrgResourceSerializerMixin", "OrgResourceSerializerMixin", "BulkOrgResourceSerializerMixin",
"BulkOrgResourceModelSerializer", "OrgMembershipSerializerMixin" "BulkOrgResourceModelSerializer", "OrgMembershipSerializerMixin",
"OrgResourceModelSerializerMixin",
] ]
...@@ -42,6 +43,10 @@ class OrgResourceSerializerMixin(serializers.Serializer): ...@@ -42,6 +43,10 @@ class OrgResourceSerializerMixin(serializers.Serializer):
return fields return fields
class OrgResourceModelSerializerMixin(OrgResourceSerializerMixin, serializers.ModelSerializer):
pass
class BulkOrgResourceSerializerMixin(BulkSerializerMixin, OrgResourceSerializerMixin): class BulkOrgResourceSerializerMixin(BulkSerializerMixin, OrgResourceSerializerMixin):
pass pass
......
...@@ -18,6 +18,8 @@ def get_org_from_request(request): ...@@ -18,6 +18,8 @@ def get_org_from_request(request):
def set_current_org(org): def set_current_org(org):
if isinstance(org, str):
org = Organization.get_instance(org)
setattr(thread_local, 'current_org_id', org.id) setattr(thread_local, 'current_org_id', org.id)
......
...@@ -180,6 +180,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView): ...@@ -180,6 +180,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
users = serializer.validated_data.get('users') users = serializer.validated_data.get('users')
if users: if users:
perm.users.remove(*tuple(users)) perm.users.remove(*tuple(users))
perm.save()
return Response({"msg": "ok"}) return Response({"msg": "ok"})
else: else:
return Response({"error": serializer.errors}) return Response({"error": serializer.errors})
...@@ -197,6 +198,7 @@ class AssetPermissionAddUserApi(RetrieveUpdateAPIView): ...@@ -197,6 +198,7 @@ class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
users = serializer.validated_data.get('users') users = serializer.validated_data.get('users')
if users: if users:
perm.users.add(*tuple(users)) perm.users.add(*tuple(users))
perm.save()
return Response({"msg": "ok"}) return Response({"msg": "ok"})
else: else:
return Response({"error": serializer.errors}) return Response({"error": serializer.errors})
...@@ -217,6 +219,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView): ...@@ -217,6 +219,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
assets = serializer.validated_data.get('assets') assets = serializer.validated_data.get('assets')
if assets: if assets:
perm.assets.remove(*tuple(assets)) perm.assets.remove(*tuple(assets))
perm.save()
return Response({"msg": "ok"}) return Response({"msg": "ok"})
else: else:
return Response({"error": serializer.errors}) return Response({"error": serializer.errors})
...@@ -234,6 +237,7 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView): ...@@ -234,6 +237,7 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
assets = serializer.validated_data.get('assets') assets = serializer.validated_data.get('assets')
if assets: if assets:
perm.assets.add(*tuple(assets)) perm.assets.add(*tuple(assets))
perm.save()
return Response({"msg": "ok"}) return Response({"msg": "ok"})
else: else:
return Response({"error": serializer.errors}) return Response({"error": serializer.errors})
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import uuid
from django.db.models import Q
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from assets.utils import LabelFilterMixin
from common.permissions import IsValidUser, IsOrgAdminOrAppUser from common.permissions import IsValidUser, IsOrgAdminOrAppUser
from common.utils import get_logger from common.utils import get_logger
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from ..hands import User, Asset, SystemUser from ..hands import User, UserGroup
from .. import serializers
logger = get_logger(__name__) logger = get_logger(__name__)
__all__ = [ __all__ = [
'UserPermissionMixin', 'UserPermissionMixin', 'UserGroupPermissionMixin',
] ]
...@@ -45,101 +41,12 @@ class UserPermissionMixin: ...@@ -45,101 +41,12 @@ class UserPermissionMixin:
return super().get_permissions() return super().get_permissions()
class GrantAssetsMixin(LabelFilterMixin): class UserGroupPermissionMixin:
serializer_class = serializers.AssetGrantedSerializer obj = None
def get_serializer_queryset(self, queryset):
assets_ids = []
system_users_ids = set()
for asset in queryset:
assets_ids.append(asset["id"])
system_users_ids.update(set(asset["system_users"]))
assets = Asset.objects.filter(id__in=assets_ids).only(
*self.serializer_class.Meta.only_fields
)
assets_map = {asset.id: asset for asset in assets}
system_users = SystemUser.objects.filter(id__in=system_users_ids).only(
*self.serializer_class.system_users_only_fields
)
system_users_map = {s.id: s for s in system_users}
data = []
for item in queryset:
i = item["id"]
asset = assets_map.get(i)
if not asset:
continue
_system_users = item["system_users"]
system_users_granted = []
for sid, action in _system_users.items():
system_user = system_users_map.get(sid)
if not system_user:
continue
if not asset.has_protocol(system_user.protocol):
continue
system_user.actions = action
system_users_granted.append(system_user)
asset.system_users_granted = system_users_granted
data.append(asset)
return data
def get_serializer(self, assets_items=None, many=True):
if assets_items is None:
assets_items = []
assets_items = self.get_serializer_queryset(assets_items)
return super().get_serializer(assets_items, many=many)
def filter_queryset_by_id(self, assets_items):
i = self.request.query_params.get("id")
if not i:
return assets_items
try:
pk = uuid.UUID(i)
except ValueError:
return assets_items
assets_map = {asset['id']: asset for asset in assets_items}
if pk in assets_map:
return [assets_map.get(pk)]
else:
return []
def search_queryset(self, assets_items):
search = self.request.query_params.get("search")
if not search:
return assets_items
assets_map = {asset['id']: asset for asset in assets_items}
assets_ids = set(assets_map.keys())
assets_ids_search = Asset.objects.filter(id__in=assets_ids).filter(
Q(hostname__icontains=search) | Q(ip__icontains=search)
).values_list('id', flat=True)
return [assets_map.get(asset_id) for asset_id in assets_ids_search]
def filter_queryset_by_label(self, assets_items):
labels_id = self.get_filter_labels_ids()
if not labels_id:
return assets_items
assets_map = {asset['id']: asset for asset in assets_items}
assets_matched = Asset.objects.filter(id__in=assets_map.keys())
for label_id in labels_id:
assets_matched = assets_matched.filter(labels=label_id)
assets_ids_matched = assets_matched.values_list('id', flat=True)
return [assets_map.get(asset_id) for asset_id in assets_ids_matched]
def sort_queryset(self, assets_items): def get_obj(self):
order_by = self.request.query_params.get('order', 'hostname') user_group_id = self.kwargs.get('pk', '')
user_group = get_object_or_404(UserGroup, id=user_group_id)
return user_group
if order_by not in ['hostname', '-hostname', 'ip', '-ip']:
order_by = 'hostname'
assets_map = {asset['id']: asset for asset in assets_items}
assets_ids_search = Asset.objects.filter(id__in=assets_map.keys())\
.order_by(order_by)\
.values_list('id', flat=True)
return [assets_map.get(asset_id) for asset_id in assets_ids_search]
def filter_queryset(self, assets_items):
assets_items = self.filter_queryset_by_id(assets_items)
assets_items = self.search_queryset(assets_items)
assets_items = self.filter_queryset_by_label(assets_items)
assets_items = self.sort_queryset(assets_items)
return assets_items
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.shortcuts import get_object_or_404
from ..hands import UserGroup
from . import user_permission as uapi from . import user_permission as uapi
from .mixin import UserGroupPermissionMixin
__all__ = [ __all__ = [
'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi', 'UserGroupGrantedAssetsApi', 'UserGroupGrantedNodesApi',
...@@ -17,15 +15,6 @@ __all__ = [ ...@@ -17,15 +15,6 @@ __all__ = [
] ]
class UserGroupPermissionMixin:
obj = None
def get_object(self):
user_group_id = self.kwargs.get('pk', '')
user_group = get_object_or_404(UserGroup, id=user_group_id)
return user_group
class UserGroupGrantedAssetsApi(UserGroupPermissionMixin, uapi.UserGrantedAssetsApi): class UserGroupGrantedAssetsApi(UserGroupPermissionMixin, uapi.UserGrantedAssetsApi):
pass pass
......
...@@ -51,8 +51,7 @@ class GetUserAssetPermissionActionsApi(UserAssetPermissionMixin, ...@@ -51,8 +51,7 @@ class GetUserAssetPermissionActionsApi(UserAssetPermissionMixin,
asset = get_object_or_404(Asset, id=asset_id) asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_id) system_user = get_object_or_404(SystemUser, id=system_id)
system_users_actions = self.util.get_asset_system_users_with_actions( system_users_actions = self.util.get_asset_system_users_with_actions(asset)
asset)
actions = system_users_actions.get(system_user) actions = system_users_actions.get(system_user)
return {"actions": actions} return {"actions": actions}
...@@ -103,8 +102,7 @@ class UserGrantedAssetSystemUsersApi(UserAssetPermissionMixin, ListAPIView): ...@@ -103,8 +102,7 @@ class UserGrantedAssetSystemUsersApi(UserAssetPermissionMixin, ListAPIView):
def get_queryset(self): def get_queryset(self):
asset_id = self.kwargs.get('asset_id') asset_id = self.kwargs.get('asset_id')
asset = get_object_or_404(Asset, id=asset_id) asset = get_object_or_404(Asset, id=asset_id)
system_users_with_actions = self.util.get_asset_system_users_with_actions( system_users_with_actions = self.util.get_asset_system_users_with_actions(asset)
asset)
system_users = [] system_users = []
for system_user, actions in system_users_with_actions.items(): for system_user, actions in system_users_with_actions.items():
system_user.actions = actions system_user.actions = actions
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from common.utils import lazyproperty
from common.tree import TreeNodeSerializer
from ..mixin import UserPermissionMixin from ..mixin import UserPermissionMixin
from ...utils import AssetPermissionUtilV2, ParserNode from ...utils import AssetPermissionUtilV2, ParserNode
from ...hands import Node from ...hands import Node, Asset
from common.tree import TreeNodeSerializer
class UserAssetPermissionMixin(UserPermissionMixin): class UserAssetPermissionMixin(UserPermissionMixin):
util = None util = None
tree = None
def initial(self, *args, **kwargs): @lazyproperty
super().initial(*args, *kwargs) def util(self):
cache_policy = self.request.query_params.get('cache_policy', '0') cache_policy = self.request.query_params.get('cache_policy', '0')
system_user_id = self.request.query_params.get("system_user") system_user_id = self.request.query_params.get("system_user")
self.util = AssetPermissionUtilV2(self.obj, cache_policy=cache_policy) util = AssetPermissionUtilV2(self.obj, cache_policy=cache_policy)
if system_user_id: if system_user_id:
self.util.filter_permissions(system_users=system_user_id) util.filter_permissions(system_users=system_user_id)
self.tree = self.util.get_user_tree() return util
@lazyproperty
def tree(self):
return self.util.get_user_tree()
class UserNodeTreeMixin: class UserNodeTreeMixin:
...@@ -41,7 +45,9 @@ class UserNodeTreeMixin: ...@@ -41,7 +45,9 @@ class UserNodeTreeMixin:
queryset = self.parse_nodes_to_queryset(queryset) queryset = self.parse_nodes_to_queryset(queryset)
return queryset return queryset
def get_serializer(self, queryset, many=True, **kwargs): def get_serializer(self, queryset=None, many=True, **kwargs):
if queryset is None:
queryset = Node.objects.none()
queryset = self.get_serializer_queryset(queryset) queryset = self.get_serializer_queryset(queryset)
queryset.sort() queryset.sort()
return super().get_serializer(queryset, many=many, **kwargs) return super().get_serializer(queryset, many=many, **kwargs)
...@@ -64,7 +70,9 @@ class UserAssetTreeMixin: ...@@ -64,7 +70,9 @@ class UserAssetTreeMixin:
_queryset = self.parse_assets_to_queryset(queryset, None) _queryset = self.parse_assets_to_queryset(queryset, None)
return _queryset return _queryset
def get_serializer(self, queryset, many=True, **kwargs): def get_serializer(self, queryset=None, many=True, **kwargs):
if queryset is None:
queryset = Asset.objects.none()
queryset = self.get_serializer_queryset(queryset) queryset = self.get_serializer_queryset(queryset)
queryset.sort() queryset.sort()
return super().get_serializer(queryset, many=many, **kwargs) return super().get_serializer(queryset, many=many, **kwargs)
...@@ -7,7 +7,7 @@ from rest_framework.generics import ( ...@@ -7,7 +7,7 @@ from rest_framework.generics import (
) )
from common.permissions import IsOrgAdminOrAppUser from common.permissions import IsOrgAdminOrAppUser
from common.utils import get_logger from common.utils import get_logger, timeit
from ...hands import Node from ...hands import Node
from ... import serializers from ... import serializers
from .mixin import UserAssetPermissionMixin, UserAssetTreeMixin from .mixin import UserAssetPermissionMixin, UserAssetTreeMixin
......
...@@ -19,18 +19,6 @@ permission_m2m_senders = ( ...@@ -19,18 +19,6 @@ permission_m2m_senders = (
) )
@on_transaction_commit
def on_permission_m2m_change(sender, action='', **kwargs):
if not action.startswith('post'):
return
logger.debug('Asset permission m2m changed, refresh user tree cache')
AssetPermissionUtilV2.expire_all_user_tree_cache()
for sender in permission_m2m_senders:
m2m_changed.connect(on_permission_m2m_change, sender=sender)
@receiver([post_save, post_delete], sender=AssetPermission) @receiver([post_save, post_delete], sender=AssetPermission)
@on_transaction_commit @on_transaction_commit
def on_permission_change(sender, action='', **kwargs): def on_permission_change(sender, action='', **kwargs):
......
# coding: utf-8 # coding: utf-8
import time
import pickle import pickle
import threading
from collections import defaultdict from collections import defaultdict
from functools import reduce from functools import reduce
from hashlib import md5 from hashlib import md5
...@@ -12,7 +12,7 @@ from django.db.models import Q ...@@ -12,7 +12,7 @@ from django.db.models import Q
from django.conf import settings from django.conf import settings
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from common.utils import get_logger, timeit from common.utils import get_logger, timeit, lazyproperty
from common.tree import TreeNode from common.tree import TreeNode
from assets.utils import TreeService from assets.utils import TreeService
from ..models import AssetPermission from ..models import AssetPermission
...@@ -126,27 +126,30 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -126,27 +126,30 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
'comment', 'is_active', 'os', 'org_id' 'comment', 'is_active', 'os', 'org_id'
) )
def __init__(self, obj, cache_policy='0'): def __init__(self, obj=None, cache_policy='0'):
self.object = obj self.object = obj
self.cache_policy = cache_policy self.cache_policy = cache_policy
self.obj_id = str(obj.id) self.obj_id = str(obj.id) if obj else None
self._permissions = None self._permissions = None
self._permissions_id = None # 标记_permission的唯一值
self._filter_id = 'None' # 当通过filter更改 permission是标记 self._filter_id = 'None' # 当通过filter更改 permission是标记
self.change_org_if_need() self.change_org_if_need()
self._user_tree = None self._user_tree = None
self._user_tree_filter_id = 'None' self._user_tree_filter_id = 'None'
self.full_tree = Node.tree()
self.mutex = threading.Lock()
@staticmethod @staticmethod
def change_org_if_need(): def change_org_if_need():
set_to_root_org() set_to_root_org()
@lazyproperty
def full_tree(self):
return Node.tree()
@property @property
def permissions(self): def permissions(self):
if self._permissions: if self._permissions:
return self._permissions return self._permissions
if self.object is None:
return AssetPermission.objects.none()
object_cls = self.object.__class__.__name__ object_cls = self.object.__class__.__name__
func = self.get_permissions_map[object_cls] func = self.get_permissions_map[object_cls]
permissions = func(self.object) permissions = func(self.object)
...@@ -159,7 +162,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -159,7 +162,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
self._permissions = self.permissions.filter(**filters) self._permissions = self.permissions.filter(**filters)
self._filter_id = md5(filters_json.encode()).hexdigest() self._filter_id = md5(filters_json.encode()).hexdigest()
@property @lazyproperty
def user_tree(self): def user_tree(self):
return self.get_user_tree() return self.get_user_tree()
...@@ -303,7 +306,6 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -303,7 +306,6 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
@timeit @timeit
def get_user_tree(self): def get_user_tree(self):
# 使用锁,保证多次获取tree的时候顺序执行,可以使用缓存 # 使用锁,保证多次获取tree的时候顺序执行,可以使用缓存
with self.mutex:
user_tree = self.get_user_tree_from_local() user_tree = self.get_user_tree_from_local()
if user_tree: if user_tree:
return user_tree return user_tree
...@@ -332,17 +334,13 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -332,17 +334,13 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
for node in nodes: for node in nodes:
ancestor_keys = node.get_ancestor_keys(with_self=True) ancestor_keys = node.get_ancestor_keys(with_self=True)
nodes_keys_related.update(set(ancestor_keys)) nodes_keys_related.update(set(ancestor_keys))
pattern = []
for key in nodes_keys_related:
pattern.append(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
kwargs = {"assets": asset} kwargs = {"assets": asset}
if pattern: if nodes_keys_related:
kwargs["nodes__key__regex"] = pattern kwargs["nodes__key__in"] = nodes_keys_related
queryset = self.permissions queryset = self.permissions
if len(kwargs) == 1: if kwargs == 1:
queryset = queryset.filter(**kwargs) queryset = queryset.filter(**kwargs)
elif len(kwargs) > 1: elif len(kwargs) > 1:
kwargs = [{k: v} for k, v in kwargs.items()] kwargs = [{k: v} for k, v in kwargs.items()]
...@@ -376,33 +374,11 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -376,33 +374,11 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
nodes_keys = Node.clean_children_keys(nodes_keys) nodes_keys = Node.clean_children_keys(nodes_keys)
return nodes_keys, assets_ids return nodes_keys, assets_ids
@staticmethod
def filter_assets_by_or_kwargs(kwargs):
if len(kwargs) == 1:
queryset = Asset.objects.filter(**kwargs)
elif len(kwargs) > 1:
kwargs = [{k: v} for k, v in kwargs.items()]
args = [Q(**kw) for kw in kwargs]
args = reduce(lambda x, y: x | y, args)
queryset = Asset.objects.filter(args)
else:
queryset = Asset.objects.none()
return queryset
@timeit @timeit
def get_assets(self): def get_assets(self):
nodes_keys, assets_ids = self.get_permissions_nodes_and_assets() nodes_keys, assets_ids = self.get_permissions_nodes_and_assets()
pattern = set() queryset = Node.get_nodes_all_assets(nodes_keys, extra_assets_ids=assets_ids)
for key in nodes_keys: return queryset.valid()
pattern.add(r'^{0}$|^{0}:'.format(key))
pattern = '|'.join(list(pattern))
kwargs = {}
if assets_ids:
kwargs["id__in"] = assets_ids
if pattern:
kwargs["nodes__key__regex"] = pattern
queryset = self.filter_assets_by_or_kwargs(kwargs)
return queryset.valid().distinct()
def get_nodes_assets(self, node, deep=False): def get_nodes_assets(self, node, deep=False):
if deep: if deep:
...@@ -410,7 +386,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin): ...@@ -410,7 +386,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
else: else:
assets_ids = self.user_tree.assets(node.key) assets_ids = self.user_tree.assets(node.key)
queryset = Asset.objects.filter(id__in=assets_ids) queryset = Asset.objects.filter(id__in=assets_ids)
return queryset.valid().distinct() return queryset.valid()
def get_nodes(self): def get_nodes(self):
return [n.identifier for n in self.user_tree.all_nodes_itr()] return [n.identifier for n in self.user_tree.all_nodes_itr()]
......
...@@ -97,6 +97,8 @@ class LDAPUserListApi(generics.ListAPIView): ...@@ -97,6 +97,8 @@ class LDAPUserListApi(generics.ListAPIView):
serializer_class = LDAPUserSerializer serializer_class = LDAPUserSerializer
def get_queryset(self): def get_queryset(self):
if hasattr(self, 'swagger_fake_view'):
return []
util = LDAPUtil() util = LDAPUtil()
try: try:
users = util.search_user_items() users = util.search_user_items()
......
...@@ -153,6 +153,7 @@ function activeNav() { ...@@ -153,6 +153,7 @@ function activeNav() {
} else { } else {
$("#" + app).addClass('active'); $("#" + app).addClass('active');
$('#' + app + ' #' + resource).addClass('active'); $('#' + app + ' #' + resource).addClass('active');
$('#' + app + ' #' + resource.replace('-', '_')).addClass('active');
} }
} }
...@@ -1138,7 +1139,10 @@ function timeOffset(a, b) { ...@@ -1138,7 +1139,10 @@ function timeOffset(a, b) {
var start = safeDate(a); var start = safeDate(a);
var end = safeDate(b); var end = safeDate(b);
var offset = (end - start) / 1000; var offset = (end - start) / 1000;
return readableSecond(offset)
}
function readableSecond(offset) {
var days = offset / 3600 / 24; var days = offset / 3600 / 24;
var hours = offset / 3600; var hours = offset / 3600;
var minutes = offset / 60; var minutes = offset / 60;
......
...@@ -114,6 +114,9 @@ ...@@ -114,6 +114,9 @@
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li> <li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li>
<li id="command-execution"><a href="{% url 'ops:command-execution-start' %}">{% trans 'Batch command' %}</a></li> <li id="command-execution"><a href="{% url 'ops:command-execution-start' %}">{% trans 'Batch command' %}</a></li>
{% if request.user.is_superuser %}
<li><a href="{% url 'flower-view' path='' %}" target="_blank" >{% trans 'Task monitor' %}</a></li>
{% endif %}
</ul> </ul>
</li> </li>
{% endif %} {% endif %}
...@@ -171,7 +174,5 @@ ...@@ -171,7 +174,5 @@
<script> <script>
$(document).ready(function () { $(document).ready(function () {
var current_org = '{{ CURRENT_ORG.name }}';
console.log(current_org);
}) })
</script> </script>
\ No newline at end of file
...@@ -58,5 +58,5 @@ class TerminalSerializer(serializers.ModelSerializer): ...@@ -58,5 +58,5 @@ class TerminalSerializer(serializers.ModelSerializer):
class TerminalRegistrationSerializer(serializers.Serializer): class TerminalRegistrationSerializer(serializers.Serializer):
name = serializers.CharField(max_length=128) name = serializers.CharField(max_length=128)
comment = serializers.CharField(max_length=128) comment = serializers.CharField(max_length=128, )
service_account = ServiceAccountSerializer(read_only=True) service_account = ServiceAccountSerializer(read_only=True)
...@@ -14,7 +14,7 @@ from common.permissions import ( ...@@ -14,7 +14,7 @@ from common.permissions import (
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser, IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser,
CanUpdateDeleteUser, CanUpdateDeleteUser,
) )
from common.mixins import IDInCacheFilterMixin from common.mixins import CommonApiMixin
from common.utils import get_logger from common.utils import get_logger
from orgs.utils import current_org from orgs.utils import current_org
from .. import serializers from .. import serializers
...@@ -30,7 +30,7 @@ __all__ = [ ...@@ -30,7 +30,7 @@ __all__ = [
] ]
class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet): class UserViewSet(CommonApiMixin, BulkModelViewSet):
filter_fields = ('username', 'email', 'name', 'id') filter_fields = ('username', 'email', 'name', 'id')
search_fields = filter_fields search_fields = filter_fields
queryset = User.objects.exclude(role=User.ROLE_APP) queryset = User.objects.exclude(role=User.ROLE_APP)
......
...@@ -72,16 +72,10 @@ function initTable() { ...@@ -72,16 +72,10 @@ function initTable() {
$(td).html(cellData); $(td).html(cellData);
}}, }},
{targets: 3, createdCell: function (td, cellData) { {targets: 3, createdCell: function (td, cellData) {
function success(systemUsers) { var innerHtml = '<a class="btn-show-system-users" data-aid="99999999"> {% trans "Show" %} </a>'
var users = []; .replace('99999999', cellData);
$.each(systemUsers, function (id, data) { $(td).html(innerHtml);
var name = htmlEscape(data.name);
users.push(name);
});
$(td).html(users.join(','))
}
$(td).html("{% trans 'Loading' %}");
getGrantedAssetSystemUsers(cellData, success)
}}, }},
], ],
ajax_url: assetTableUrl, ajax_url: assetTableUrl,
...@@ -185,5 +179,19 @@ $(document).ready(function () { ...@@ -185,5 +179,19 @@ $(document).ready(function () {
var val = $(this).text(); var val = $(this).text();
$("#user_assets_table_filter input").val(val); $("#user_assets_table_filter input").val(val);
assetTable.search(val).draw(); assetTable.search(val).draw();
}) }).on('click', '.btn-show-system-users', function () {
var $this = $(this);
var assetId = $this.data('aid');
function success(systemUsers) {
var users = [];
$.each(systemUsers, function (id, data) {
var name = htmlEscape(data.name);
users.push(name);
});
$this.parent().html(users.join(','))
}
getGrantedAssetSystemUsers(assetId, success)
})
</script> </script>
...@@ -33,9 +33,9 @@ ...@@ -33,9 +33,9 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1"; var assetTableUrl = "{% url 'api-perms:user-assets' pk=object.id %}?cache_policy=1";
var selectUrl = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?all=1'; var selectUrl = '{% url "api-perms:user-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1&all=1';
var treeUrl = "{% url 'api-perms:user-nodes-children-as-tree' pk=object.id %}?cache_policy=1"; var treeUrl = "{% url 'api-perms:user-nodes-children-as-tree' pk=object.id %}?cache_policy=1";
var systemUsersUrl = "{% url 'api-perms:user-asset-system-users' pk=object.id asset_id=DEFAULT_PK %}"; var systemUsersUrl = "{% url 'api-perms:user-asset-system-users' pk=object.id asset_id=DEFAULT_PK %}?cache_policy=1";
$(document).ready(function () { $(document).ready(function () {
initTree(); initTree();
......
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var assetTableUrl = "{% url 'api-perms:user-group-assets' pk=object.id %}?cache_policy=1"; var assetTableUrl = "{% url 'api-perms:user-group-assets' pk=object.id %}?cache_policy=1";
var selectUrl = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}?all=1'; var selectUrl = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}?&cache_policy=1&all=1';
var treeUrl = "{% url 'api-perms:user-group-nodes-children-as-tree' pk=object.id %}?cache_policy=1"; var treeUrl = "{% url 'api-perms:user-group-nodes-children-as-tree' pk=object.id %}?cache_policy=1";
var systemUsersUrl = "{% url 'api-perms:user-group-asset-system-users' pk=object.id asset_id=DEFAULT_PK %}"; var systemUsersUrl = "{% url 'api-perms:user-group-asset-system-users' pk=object.id asset_id=DEFAULT_PK %}?cache_policy=1";
var showAssetHref = true; // Need input default true var showAssetHref = true; // Need input default true
......
...@@ -200,9 +200,13 @@ def is_running(s, unlink=True): ...@@ -200,9 +200,13 @@ def is_running(s, unlink=True):
def parse_service(s): def parse_service(s):
all_services = ['gunicorn', 'celery_ansible', 'celery_default', 'beat'] all_services = [
'gunicorn', 'celery_ansible', 'celery_default', 'beat', 'flower'
]
if s == 'all': if s == 'all':
return all_services return all_services
elif s == 'gunicorn':
return ['gunicorn', 'flower']
elif s == "celery": elif s == "celery":
return ["celery_ansible", "celery_default"] return ["celery_ansible", "celery_default"]
elif "," in s: elif "," in s:
...@@ -240,17 +244,19 @@ def get_start_gunicorn_kwargs(): ...@@ -240,17 +244,19 @@ def get_start_gunicorn_kwargs():
def get_start_celery_ansible_kwargs(): def get_start_celery_ansible_kwargs():
print("\n- Start Celery as Distributed Task Queue") print("\n- Start Celery as Distributed Task Queue: Ansible")
return get_start_worker_kwargs('ansible', 4) return get_start_worker_kwargs('ansible', 4)
def get_start_celery_default_kwargs(): def get_start_celery_default_kwargs():
print("\n- Start Celery as Distributed Task Queue: Celery")
return get_start_worker_kwargs('celery', 2) return get_start_worker_kwargs('celery', 2)
def get_start_worker_kwargs(queue, num): def get_start_worker_kwargs(queue, num):
# Todo: Must set this environment, otherwise not no ansible result return # Todo: Must set this environment, otherwise not no ansible result return
os.environ.setdefault('PYTHONOPTIMIZE', '1') os.environ.setdefault('PYTHONOPTIMIZE', '1')
os.environ.setdefault('ANSIBLE_FORCE_COLOR', 'True')
if os.getuid() == 0: if os.getuid() == 0:
os.environ.setdefault('C_FORCE_ROOT', '1') os.environ.setdefault('C_FORCE_ROOT', '1')
...@@ -261,6 +267,24 @@ def get_start_worker_kwargs(queue, num): ...@@ -261,6 +267,24 @@ def get_start_worker_kwargs(queue, num):
'-l', 'INFO', '-l', 'INFO',
'-c', str(num), '-c', str(num),
'-Q', queue, '-Q', queue,
'-n', '{}@%h'.format(queue)
]
return {"cmd": cmd, "cwd": APPS_DIR}
def get_start_flower_kwargs():
print("\n- Start Flower as Task Monitor")
if os.getuid() == 0:
os.environ.setdefault('C_FORCE_ROOT', '1')
cmd = [
'celery', 'flower',
'-A', 'ops',
'-l', 'INFO',
'--url_prefix=flower',
'--auto_refresh=False',
'--max_tasks=1000',
'--tasks_columns=uuid,name,args,state,received,started,runtime,worker'
] ]
return {"cmd": cmd, "cwd": APPS_DIR} return {"cmd": cmd, "cwd": APPS_DIR}
...@@ -333,6 +357,7 @@ def start_service(s): ...@@ -333,6 +357,7 @@ def start_service(s):
"celery_ansible": get_start_celery_ansible_kwargs, "celery_ansible": get_start_celery_ansible_kwargs,
"celery_default": get_start_celery_default_kwargs, "celery_default": get_start_celery_default_kwargs,
"beat": get_start_beat_kwargs, "beat": get_start_beat_kwargs,
"flower": get_start_flower_kwargs,
} }
kwargs = services_kwargs.get(s)() kwargs = services_kwargs.get(s)()
...@@ -449,7 +474,7 @@ if __name__ == '__main__': ...@@ -449,7 +474,7 @@ if __name__ == '__main__':
) )
parser.add_argument( parser.add_argument(
"service", type=str, default="all", nargs="?", "service", type=str, default="all", nargs="?",
choices=("all", "gunicorn", "celery", "beat", "celery,beat"), choices=("all", "gunicorn", "celery", "beat", "celery,beat", "flower"),
help="The service to start", help="The service to start",
) )
parser.add_argument('-d', '--daemon', nargs="?", const=1) parser.add_argument('-d', '--daemon', nargs="?", const=1)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment