Commit ddafd7ba authored by 老广's avatar 老广 Committed by BaiJiangJie

Dev ansible windows 2 (#2783)

* [Update] 改密支持windows

* [Update] 修改asset表结构

* [Feature] Windows支持批量改密、测试可连接性等功能

* [Update] 处理创建资产时labels的问题

* [Update] 优化测试管理系统、系统用户可连接性任务执行逻辑

* [Update] 优化ansible任务逻辑;添加自动推送rdp系统用户功能

* [Update] 添加翻译

* [Update] 优化ansible任务逻辑(测试系统用户可连接性, 通过协议过滤资产)

* [Update] 更新翻译

* [Update] 更新翻译

* [Update] 推送windows系统用户,默认添加到Users、Remote Desktop Users组中

* [Update] 优化小细节

* [Update] 更新翻译,删除多余代码

* [Update] 更新翻译信息
parent 9f9f2254
...@@ -16,7 +16,7 @@ from django.urls import reverse_lazy ...@@ -16,7 +16,7 @@ from django.urls import reverse_lazy
from django.core.cache import cache from django.core.cache import cache
from django.db.models import Q from django.db.models import Q
from common.mixins import IDInCacheFilterMixin from common.mixins import IDInCacheFilterMixin, ApiMessageMixin
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
...@@ -36,7 +36,7 @@ __all__ = [ ...@@ -36,7 +36,7 @@ __all__ = [
] ]
class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet): class AssetViewSet(IDInCacheFilterMixin, LabelFilter, ApiMessageMixin, BulkModelViewSet):
""" """
API endpoint that allows Asset to be viewed or edited. API endpoint that allows Asset to be viewed or edited.
""" """
...@@ -47,6 +47,7 @@ class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet): ...@@ -47,6 +47,7 @@ class AssetViewSet(IDInCacheFilterMixin, LabelFilter, BulkModelViewSet):
serializer_class = serializers.AssetSerializer serializer_class = serializers.AssetSerializer
pagination_class = LimitOffsetPagination pagination_class = LimitOffsetPagination
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
success_message = _("%(hostname)s was %(action)s successfully")
def set_assets_node(self, assets): def set_assets_node(self, assets):
if not isinstance(assets, list): if not isinstance(assets, list):
...@@ -169,8 +170,8 @@ class AssetGatewayApi(generics.RetrieveAPIView): ...@@ -169,8 +170,8 @@ class AssetGatewayApi(generics.RetrieveAPIView):
asset = get_object_or_404(Asset, pk=asset_id) asset = get_object_or_404(Asset, pk=asset_id)
if asset.domain and \ if asset.domain and \
asset.domain.gateways.filter(protocol=asset.protocol).exists(): asset.domain.gateways.filter(protocol='ssh').exists():
gateway = random.choice(asset.domain.gateways.filter(protocol=asset.protocol)) gateway = random.choice(asset.domain.gateways.filter(protocol='ssh'))
serializer = serializers.GatewayWithAuthSerializer(instance=gateway) serializer = serializers.GatewayWithAuthSerializer(instance=gateway)
return Response(serializer.data) return Response(serializer.data)
else: else:
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.utils.translation import ugettext_lazy as _
UPDATE_ASSETS_HARDWARE_TASKS = [ UPDATE_ASSETS_HARDWARE_TASKS = [
{ {
...@@ -22,6 +20,14 @@ TEST_ADMIN_USER_CONN_TASKS = [ ...@@ -22,6 +20,14 @@ TEST_ADMIN_USER_CONN_TASKS = [
} }
} }
] ]
TEST_WINDOWS_ADMIN_USER_CONN_TASKS = [
{
"name": "ping",
"action": {
"module": "win_ping",
}
}
]
ASSET_ADMIN_CONN_CACHE_KEY = "ASSET_ADMIN_USER_CONN_{}" ASSET_ADMIN_CONN_CACHE_KEY = "ASSET_ADMIN_USER_CONN_{}"
...@@ -34,7 +40,14 @@ TEST_SYSTEM_USER_CONN_TASKS = [ ...@@ -34,7 +40,14 @@ TEST_SYSTEM_USER_CONN_TASKS = [
} }
} }
] ]
TEST_WINDOWS_SYSTEM_USER_CONN_TASKS = [
{
"name": "ping",
"action": {
"module": "win_ping",
}
}
]
ASSET_USER_CONN_CACHE_KEY = 'ASSET_USER_CONN_{}_{}' ASSET_USER_CONN_CACHE_KEY = 'ASSET_USER_CONN_{}_{}'
TEST_ASSET_USER_CONN_TASKS = [ TEST_ASSET_USER_CONN_TASKS = [
...@@ -45,6 +58,14 @@ TEST_ASSET_USER_CONN_TASKS = [ ...@@ -45,6 +58,14 @@ TEST_ASSET_USER_CONN_TASKS = [
} }
} }
] ]
TEST_WINDOWS_ASSET_USER_CONN_TASKS = [
{
"name": "ping",
"action": {
"module": "win_ping",
}
}
]
TASK_OPTIONS = { TASK_OPTIONS = {
......
...@@ -6,21 +6,39 @@ from django.utils.translation import gettext_lazy as _ ...@@ -6,21 +6,39 @@ from django.utils.translation import gettext_lazy as _
from common.utils import get_logger from common.utils import get_logger
from orgs.mixins import OrgModelForm from orgs.mixins import OrgModelForm
from ..models import Asset, AdminUser from ..models import Asset, AdminUser, Protocol
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm'] __all__ = [
'AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm',
'ProtocolForm'
]
class ProtocolForm(forms.ModelForm):
class Meta:
model = Protocol
fields = ['name', 'port']
widgets = {
'name': forms.Select(attrs={
'class': 'form-control protocol-name'
}),
'port': forms.TextInput(attrs={
'class': 'form-control protocol-port'
}),
}
class AssetCreateForm(OrgModelForm): class AssetCreateForm(OrgModelForm):
PROTOCOL_CHOICES = Protocol.PROTOCOL_CHOICES
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'public_ip', 'port', 'comment', 'hostname', 'ip', 'public_ip', 'protocols', 'comment',
'nodes', 'is_active', 'admin_user', 'labels', 'platform', 'nodes', 'is_active', 'admin_user', 'labels', 'platform',
'domain', 'protocol', 'domain',
] ]
widgets = { widgets = {
'nodes': forms.SelectMultiple(attrs={ 'nodes': forms.SelectMultiple(attrs={
...@@ -32,7 +50,6 @@ class AssetCreateForm(OrgModelForm): ...@@ -32,7 +50,6 @@ class AssetCreateForm(OrgModelForm):
'labels': forms.SelectMultiple(attrs={ 'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Label') 'class': 'select2', 'data-placeholder': _('Label')
}), }),
'port': forms.TextInput(),
'domain': forms.Select(attrs={ 'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain') 'class': 'select2', 'data-placeholder': _('Domain')
}), }),
...@@ -54,9 +71,9 @@ class AssetUpdateForm(OrgModelForm): ...@@ -54,9 +71,9 @@ class AssetUpdateForm(OrgModelForm):
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'port', 'nodes', 'is_active', 'platform', 'hostname', 'ip', 'protocols', 'nodes', 'is_active', 'platform',
'public_ip', 'number', 'comment', 'admin_user', 'labels', 'public_ip', 'number', 'comment', 'admin_user', 'labels',
'domain', 'protocol', 'domain',
] ]
widgets = { widgets = {
'nodes': forms.SelectMultiple(attrs={ 'nodes': forms.SelectMultiple(attrs={
...@@ -68,7 +85,6 @@ class AssetUpdateForm(OrgModelForm): ...@@ -68,7 +85,6 @@ class AssetUpdateForm(OrgModelForm):
'labels': forms.SelectMultiple(attrs={ 'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Label') 'class': 'select2', 'data-placeholder': _('Label')
}), }),
'port': forms.TextInput(),
'domain': forms.Select(attrs={ 'domain': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Domain') 'class': 'select2', 'data-placeholder': _('Domain')
}), }),
...@@ -101,8 +117,8 @@ class AssetBulkUpdateForm(OrgModelForm): ...@@ -101,8 +117,8 @@ class AssetBulkUpdateForm(OrgModelForm):
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'assets', 'port', 'admin_user', 'labels', 'platform', 'assets', 'admin_user', 'labels', 'platform',
'protocol', 'domain', 'domain',
] ]
widgets = { widgets = {
'labels': forms.SelectMultiple( 'labels': forms.SelectMultiple(
......
...@@ -100,16 +100,18 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm): ...@@ -100,16 +100,18 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
private_key, public_key = super().gen_keys() private_key, public_key = super().gen_keys()
if login_mode == SystemUser.LOGIN_MANUAL or \ if login_mode == SystemUser.LOGIN_MANUAL or \
protocol in [SystemUser.PROTOCOL_RDP, protocol in [SystemUser.PROTOCOL_TELNET,
SystemUser.PROTOCOL_TELNET,
SystemUser.PROTOCOL_VNC]: SystemUser.PROTOCOL_VNC]:
system_user.auto_push = 0 system_user.auto_push = 0
auto_generate_key = False
system_user.save() system_user.save()
auto_generate_key = False
if auto_generate_key: if auto_generate_key:
logger.info('Auto generate key and set system user auth') logger.info('Auto generate key and set system user auth')
system_user.auto_gen_auth() if protocol == SystemUser.PROTOCOL_SSH:
system_user.auto_gen_auth()
elif protocol == SystemUser.PROTOCOL_RDP:
system_user.auto_gen_auth_password()
else: else:
system_user.set_auth(password=password, private_key=private_key, system_user.set_auth(password=password, private_key=private_key,
public_key=public_key) public_key=public_key)
......
...@@ -15,4 +15,9 @@ class Migration(migrations.Migration): ...@@ -15,4 +15,9 @@ class Migration(migrations.Migration):
name='ip', name='ip',
field=models.CharField(db_index=True, max_length=128, verbose_name='IP'), field=models.CharField(db_index=True, max_length=128, verbose_name='IP'),
), ),
migrations.AlterField(
model_name='asset',
name='public_ip',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Public IP'),
),
] ]
# Generated by Django 2.1.7 on 2019-05-22 02:58
import django.core.validators
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0027_auto_20190521_1703'),
]
operations = [
migrations.CreateModel(
name='Protocol',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)'), ('vnc', 'vnc')], default='ssh', max_length=16, verbose_name='Name')),
('port', models.IntegerField(default=22, validators=[django.core.validators.MaxValueValidator(65535), django.core.validators.MinValueValidator(1)], verbose_name='Port')),
],
),
migrations.AddField(
model_name='asset',
name='protocols',
field=models.ManyToManyField(to='assets.Protocol',
verbose_name='Protocol'),
),
]
# Generated by Django 2.1.7 on 2019-05-22 03:14
from django.db import migrations
def migrate_assets_protocol(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
db_alias = schema_editor.connection.alias
assets = asset_model.objects.using(db_alias).all()
for asset in assets:
asset.protocols.create(name=asset.protocol, port=asset.port)
class Migration(migrations.Migration):
dependencies = [
('assets', '0028_protocol'),
]
operations = [
migrations.RunPython(migrate_assets_protocol),
]
...@@ -8,4 +8,3 @@ from .asset import * ...@@ -8,4 +8,3 @@ from .asset import *
from .cmd_filter import * from .cmd_filter import *
from .utils import * from .utils import *
from .authbook import * from .authbook import *
from applications.models.remote_app import *
...@@ -12,11 +12,12 @@ from django.db import models ...@@ -12,11 +12,12 @@ from django.db import models
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache from django.core.cache import cache
from django.core.validators import MinValueValidator, MaxValueValidator
from .user import AdminUser, SystemUser from .user import AdminUser, SystemUser
from orgs.mixins import OrgModelMixin, OrgManager from orgs.mixins import OrgModelMixin, OrgManager
__all__ = ['Asset'] __all__ = ['Asset', 'Protocol']
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -47,18 +48,13 @@ class AssetQuerySet(models.QuerySet): ...@@ -47,18 +48,13 @@ class AssetQuerySet(models.QuerySet):
return self.active() return self.active()
class Asset(OrgModelMixin): class AssetManager(OrgManager):
# Important def get_queryset(self):
PLATFORM_CHOICES = ( queryset = super().get_queryset().prefetch_related("nodes", "protocols")
('Linux', 'Linux'), return queryset
('Unix', 'Unix'),
('MacOS', 'MacOS'),
('BSD', 'BSD'),
('Windows', 'Windows'),
('Windows2016', 'Windows(2016)'),
('Other', 'Other'),
)
class Protocol(models.Model):
PROTOCOL_SSH = 'ssh' PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp' PROTOCOL_RDP = 'rdp'
PROTOCOL_TELNET = 'telnet' PROTOCOL_TELNET = 'telnet'
...@@ -69,12 +65,39 @@ class Asset(OrgModelMixin): ...@@ -69,12 +65,39 @@ class Asset(OrgModelMixin):
(PROTOCOL_TELNET, 'telnet (beta)'), (PROTOCOL_TELNET, 'telnet (beta)'),
(PROTOCOL_VNC, 'vnc'), (PROTOCOL_VNC, 'vnc'),
) )
PORT_VALIDATORS = [MaxValueValidator(65535), MinValueValidator(1)]
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=16, choices=PROTOCOL_CHOICES,
default=PROTOCOL_SSH, verbose_name=_("Name"))
port = models.IntegerField(default=22, verbose_name=_("Port"),
validators=PORT_VALIDATORS)
def __str__(self):
return "{}:{}".format(self.name, self.port)
class Asset(OrgModelMixin):
# Important
PLATFORM_CHOICES = (
('Linux', 'Linux'),
('Unix', 'Unix'),
('MacOS', 'MacOS'),
('BSD', 'BSD'),
('Windows', 'Windows'),
('Windows2016', 'Windows(2016)'),
('Other', 'Other'),
)
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True) ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
hostname = models.CharField(max_length=128, verbose_name=_('Hostname')) hostname = models.CharField(max_length=128, verbose_name=_('Hostname'))
protocol = models.CharField(max_length=128, default=PROTOCOL_SSH, choices=PROTOCOL_CHOICES, verbose_name=_('Protocol')) protocol = models.CharField(max_length=128, default=Protocol.PROTOCOL_SSH,
choices=Protocol.PROTOCOL_CHOICES,
verbose_name=_('Protocol'))
port = models.IntegerField(default=22, verbose_name=_('Port')) port = models.IntegerField(default=22, verbose_name=_('Port'))
protocols = models.ManyToManyField('Protocol', verbose_name=_("Protocol"))
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform')) platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform'))
domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL)
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes")) nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
...@@ -84,7 +107,7 @@ class Asset(OrgModelMixin): ...@@ -84,7 +107,7 @@ class Asset(OrgModelMixin):
admin_user = models.ForeignKey('assets.AdminUser', on_delete=models.PROTECT, null=True, verbose_name=_("Admin user")) admin_user = models.ForeignKey('assets.AdminUser', on_delete=models.PROTECT, null=True, verbose_name=_("Admin user"))
# Some information # Some information
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) public_ip = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Public IP'))
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
# Collect # Collect
...@@ -110,7 +133,7 @@ class Asset(OrgModelMixin): ...@@ -110,7 +133,7 @@ class Asset(OrgModelMixin):
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')) date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment')) comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
objects = OrgManager.from_queryset(AssetQuerySet)() objects = AssetManager.from_queryset(AssetQuerySet)()
CONNECTIVITY_CACHE_KEY = '_JMS_ASSET_CONNECTIVITY_{}' CONNECTIVITY_CACHE_KEY = '_JMS_ASSET_CONNECTIVITY_{}'
UNREACHABLE, REACHABLE, UNKNOWN = range(0, 3) UNREACHABLE, REACHABLE, UNKNOWN = range(0, 3)
CONNECTIVITY_CHOICES = ( CONNECTIVITY_CHOICES = (
...@@ -131,19 +154,53 @@ class Asset(OrgModelMixin): ...@@ -131,19 +154,53 @@ class Asset(OrgModelMixin):
return True, '' return True, ''
return False, warning return False, warning
def support_ansible(self): @property
if self.platform in ("Windows", "Windows2016", "Other"): def protocols_name(self):
return False names = []
if self.protocol != 'ssh': for protocol in self.protocols.all():
names.append(protocol.name)
return names
def has_protocol(self, name):
return name in self.protocols_name
def get_protocol_by_name(self, name):
for i in self.protocols.all():
if i.name.lower() == name.lower():
return i
return None
@property
def protocol_ssh(self):
return self.get_protocol_by_name("ssh")
@property
def protocol_rdp(self):
return self.get_protocol_by_name("rdp")
@property
def ssh_port(self):
if self.protocol_ssh:
port = self.protocol_ssh.port
else:
port = 22
return port
def is_windows(self):
if self.platform in ("Windows", "Windows2016"):
return True
else:
return False return False
return True
def is_unixlike(self): def is_unixlike(self):
if self.platform not in ("Windows", "Windows2016"): if self.platform not in ("Windows", "Windows2016", "Other"):
return True return True
else: else:
return False return False
def is_support_ansible(self):
return self.has_protocol('ssh') and self.platform not in ("Other",)
def get_nodes(self): def get_nodes(self):
from .node import Node from .node import Node
nodes = self.nodes.all() or [Node.root()] nodes = self.nodes.all() or [Node.root()]
...@@ -172,6 +229,15 @@ class Asset(OrgModelMixin): ...@@ -172,6 +229,15 @@ class Asset(OrgModelMixin):
filter_arg |= Q(Q(org_id__isnull=True) | Q(org_id=''), hostname__in=hosts) filter_arg |= Q(Q(org_id__isnull=True) | Q(org_id=''), hostname__in=hosts)
return Asset.objects.filter(filter_arg) return Asset.objects.filter(filter_arg)
@property
def cpu_info(self):
info = ""
if self.cpu_model:
info += self.cpu_model
if self.cpu_count and self.cpu_cores:
info += "{}*{}".format(self.cpu_count, self.cpu_cores)
return info
@property @property
def hardware_info(self): def hardware_info(self):
if self.cpu_count: if self.cpu_count:
...@@ -196,14 +262,16 @@ class Asset(OrgModelMixin): ...@@ -196,14 +262,16 @@ class Asset(OrgModelMixin):
cache.set(key, value, 3600*2) cache.set(key, value, 3600*2)
def get_auth_info(self): def get_auth_info(self):
if self.admin_user: if not self.admin_user:
self.admin_user.load_specific_asset_auth(self) return {}
return {
'username': self.admin_user.username, self.admin_user.load_specific_asset_auth(self)
'password': self.admin_user.password, info = {
'private_key': self.admin_user.private_key_file, 'username': self.admin_user.username,
'become': self.admin_user.become_info, 'password': self.admin_user.password,
} 'private_key': self.admin_user.private_key_file,
}
return info
def as_node(self): def as_node(self):
from .node import Node from .node import Node
...@@ -215,35 +283,6 @@ class Asset(OrgModelMixin): ...@@ -215,35 +283,6 @@ class Asset(OrgModelMixin):
fake_node.is_node = False fake_node.is_node = False
return fake_node return fake_node
def to_json(self):
info = {
'id': self.id,
'hostname': self.hostname,
'ip': self.ip,
'port': self.port,
}
if self.domain and self.domain.gateway_set.all():
info["gateways"] = [d.id for d in self.domain.gateway_set.all()]
return info
def _to_secret_json(self):
"""
Ansible use it create inventory
Todo: May be move to ops implements it
"""
data = self.to_json()
if self.admin_user:
self.admin_user.load_specific_asset_auth(self)
admin_user = self.admin_user
data.update({
'username': admin_user.username,
'password': admin_user.password,
'private_key': admin_user.private_key_file,
'become': admin_user.become_info,
'groups': [node.value for node in self.nodes.all()],
})
return data
def as_tree_node(self, parent_node): def as_tree_node(self, parent_node):
from common.tree import TreeNode from common.tree import TreeNode
icon_skin = 'file' icon_skin = 'file'
...@@ -265,9 +304,11 @@ class Asset(OrgModelMixin): ...@@ -265,9 +304,11 @@ class Asset(OrgModelMixin):
'id': self.id, 'id': self.id,
'hostname': self.hostname, 'hostname': self.hostname,
'ip': self.ip, 'ip': self.ip,
'port': self.port, 'protocols': [
{"name": p.name, "port": p.port}
for p in self.protocols.all()
],
'platform': self.platform, 'platform': self.platform,
'protocol': self.protocol,
} }
} }
} }
...@@ -291,10 +332,10 @@ class Asset(OrgModelMixin): ...@@ -291,10 +332,10 @@ class Asset(OrgModelMixin):
asset = cls(ip='.'.join(ip), asset = cls(ip='.'.join(ip),
hostname=forgery_py.internet.user_name(True), hostname=forgery_py.internet.user_name(True),
admin_user=choice(AdminUser.objects.all()), admin_user=choice(AdminUser.objects.all()),
port=22,
created_by='Fake') created_by='Fake')
try: try:
asset.save() asset.save()
asset.protocols.create(name="ssh", port=22)
if nodes and len(nodes) > 3: if nodes and len(nodes) > 3:
_nodes = random.sample(nodes, 3) _nodes = random.sample(nodes, 3)
else: else:
......
...@@ -158,6 +158,10 @@ class AssetUser(OrgModelMixin): ...@@ -158,6 +158,10 @@ class AssetUser(OrgModelMixin):
private_key=private_key, private_key=private_key,
public_key=public_key) public_key=public_key)
def auto_gen_auth_password(self):
password = str(uuid.uuid4())
self.set_auth(password=password)
def _to_secret_json(self): def _to_secret_json(self):
"""Push system user use it""" """Push system user use it"""
return { return {
......
...@@ -201,7 +201,7 @@ class SystemUser(AssetUser): ...@@ -201,7 +201,7 @@ class SystemUser(AssetUser):
return self.get_login_mode_display() return self.get_login_mode_display()
def is_need_push(self): def is_need_push(self):
if self.auto_push and self.protocol == self.PROTOCOL_SSH: if self.auto_push and self.protocol in [self.PROTOCOL_SSH, self.PROTOCOL_RDP]:
return True return True
else: else:
return False return False
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from rest_framework import serializers from rest_framework import serializers
from rest_framework.validators import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgResourceSerializerMixin from orgs.mixins import OrgResourceSerializerMixin
from common.mixins import BulkSerializerMixin from common.mixins import BulkSerializerMixin
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from ..models import Asset from ..models import Asset, Protocol
from .system_user import AssetSystemUserSerializer from .system_user import AssetSystemUserSerializer
__all__ = [ __all__ = [
...@@ -16,25 +17,33 @@ __all__ = [ ...@@ -16,25 +17,33 @@ __all__ = [
] ]
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResourceSerializerMixin): class ProtocolSerializer(serializers.ModelSerializer):
class Meta:
model = Protocol
fields = ["name", "port"]
class AssetSerializer(BulkSerializerMixin, OrgResourceSerializerMixin,
serializers.ModelSerializer):
protocols = ProtocolSerializer(many=True)
""" """
资产的数据结构 资产的数据结构
""" """
class Meta: class Meta:
model = Asset model = Asset
list_serializer_class = AdaptedBulkListSerializer list_serializer_class = AdaptedBulkListSerializer
# validators = [] # 解决批量导入时unique_together字段校验失败
fields = [ fields = [
'id', 'org_id', 'org_name', 'ip', 'hostname', 'protocol', 'port', 'id', 'org_id', 'org_name', 'ip', 'hostname', 'protocol', 'port',
'platform', 'is_active', 'public_ip', 'domain', 'admin_user', 'protocols', 'platform', 'is_active', 'public_ip', 'domain',
'nodes', 'labels', 'number', 'vendor', 'model', 'sn', 'admin_user', 'nodes', 'labels', 'number', 'vendor', 'model', 'sn',
'cpu_model', 'cpu_count', 'cpu_cores', 'cpu_vcpus', 'memory', 'cpu_model', 'cpu_count', 'cpu_cores', 'cpu_vcpus', 'memory',
'disk_total', 'disk_info', 'os', 'os_version', 'os_arch', 'disk_total', 'disk_info', 'os', 'os_version', 'os_arch',
'hostname_raw', 'comment', 'created_by', 'date_created', 'hostname_raw', 'comment', 'created_by', 'date_created',
'hardware_info', 'connectivity' 'hardware_info', 'connectivity'
] ]
read_only_fields = ( read_only_fields = (
'number', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count', 'vendor', 'model', 'sn', 'cpu_model', 'cpu_count',
'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info', 'cpu_cores', 'cpu_vcpus', 'memory', 'disk_total', 'disk_info',
'os', 'os_version', 'os_arch', 'hostname_raw', 'os', 'os_version', 'os_arch', 'hostname_raw',
'created_by', 'date_created', 'created_by', 'date_created',
...@@ -43,7 +52,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou ...@@ -43,7 +52,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou
'hardware_info': {'label': _('Hardware info')}, 'hardware_info': {'label': _('Hardware info')},
'connectivity': {'label': _('Connectivity')}, 'connectivity': {'label': _('Connectivity')},
'org_name': {'label': _('Org name')} 'org_name': {'label': _('Org name')}
} }
@classmethod @classmethod
...@@ -53,18 +61,64 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou ...@@ -53,18 +61,64 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer, OrgResou
.select_related('admin_user') .select_related('admin_user')
return queryset return queryset
def get_field_names(self, declared_fields, info): @staticmethod
fields = super().get_field_names(declared_fields, info) def validate_protocols(attr):
fields.extend([ protocols_name = [i.get("name", "ssh") for i in attr]
'hardware_info', 'connectivity', 'org_name' errors = [{} for i in protocols_name]
]) for i, name in enumerate(protocols_name):
return fields if name in protocols_name[:i]:
errors[i] = {"name": _("Protocol duplicate: {}").format(name)}
if any(errors):
raise ValidationError(errors)
return attr
def create(self, validated_data):
protocols_data = validated_data.pop("protocols")
# 兼容老的api
protocol = validated_data.get("protocol")
port = validated_data.get("port")
if not protocols_data and protocol and port:
protocols_data = [{"name": protocol, "port": port}]
if not protocol and not port and protocols_data:
validated_data["protocol"] = protocols_data[0]["name"]
validated_data["port"] = protocols_data[0]["port"]
protocols_serializer = ProtocolSerializer(data=protocols_data, many=True)
protocols_serializer.is_valid(raise_exception=True)
protocols = protocols_serializer.save()
instance = super().create(validated_data)
instance.protocols.set(protocols)
return instance
def update(self, instance, validated_data):
protocols_data = validated_data.pop("protocols")
# 兼容老的api
protocol = validated_data.get("protocol")
port = validated_data.get("port")
if not protocols_data and protocol and port:
protocols_data = [{"name": protocol, "port": port}]
if not protocol and not port and protocols_data:
validated_data["protocol"] = protocols_data[0]["name"]
validated_data["port"] = protocols_data[0]["port"]
protocols_serializer = ProtocolSerializer(data=protocols_data, many=True)
protocols_serializer.is_valid(raise_exception=True)
protocols = protocols_serializer.save()
instance = super().update(instance, validated_data)
instance.protocols.all().delete()
instance.protocols.set(protocols)
return instance
class AssetAsNodeSerializer(serializers.ModelSerializer): class AssetAsNodeSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Asset model = Asset
fields = ['id', 'hostname', 'ip', 'port', 'platform', 'protocol'] fields = ['id', 'hostname', 'ip', 'platform', 'protocols']
class AssetGrantedSerializer(serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
...@@ -78,9 +132,9 @@ class AssetGrantedSerializer(serializers.ModelSerializer): ...@@ -78,9 +132,9 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Asset model = Asset
fields = ( fields = (
"id", "hostname", "ip", "port", "system_users_granted", "id", "hostname", "ip", "system_users_granted",
"is_active", "system_users_join", "os", 'domain', "is_active", "system_users_join", "os", 'domain',
"platform", "comment", "protocol", "org_id", "org_name", "platform", "comment", "protocols", "org_id", "org_name",
) )
@staticmethod @staticmethod
......
...@@ -5,6 +5,7 @@ from django.db.models.signals import post_save, m2m_changed, post_delete ...@@ -5,6 +5,7 @@ from django.db.models.signals import post_save, m2m_changed, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from common.utils import get_logger from common.utils import get_logger
from common.decorator import on_transaction_commit
from .models import Asset, SystemUser, Node, AuthBook from .models import Asset, SystemUser, Node, AuthBook
from .tasks import ( from .tasks import (
update_assets_hardware_info_util, update_assets_hardware_info_util,
...@@ -32,9 +33,12 @@ def set_asset_root_node(asset): ...@@ -32,9 +33,12 @@ def set_asset_root_node(asset):
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier") @receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
@on_transaction_commit
def on_asset_created_or_update(sender, instance=None, created=False, **kwargs): def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
if created: if created:
logger.info("Asset `{}` create signal received".format(instance)) logger.info("Asset `{}` create signal received".format(instance))
# 获取资产硬件信息
update_asset_hardware_info_on_created(instance) update_asset_hardware_info_on_created(instance)
test_asset_conn_on_created(instance) test_asset_conn_on_created(instance)
......
...@@ -3,6 +3,7 @@ import json ...@@ -3,6 +3,7 @@ import json
import re import re
import os import os
from collections import defaultdict
from celery import shared_task from celery import shared_task
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.core.cache import cache from django.core.cache import cache
...@@ -31,7 +32,7 @@ def check_asset_can_run_ansible(asset): ...@@ -31,7 +32,7 @@ def check_asset_can_run_ansible(asset):
msg = _("Asset has been disabled, skipped: {}").format(asset) msg = _("Asset has been disabled, skipped: {}").format(asset)
logger.info(msg) logger.info(msg)
return False return False
if not asset.support_ansible(): if not asset.is_support_ansible():
msg = _("Asset may not be support ansible, skipped: {}").format(asset) msg = _("Asset may not be support ansible, skipped: {}").format(asset)
logger.info(msg) logger.info(msg)
return False return False
...@@ -45,10 +46,21 @@ def clean_hosts(assets): ...@@ -45,10 +46,21 @@ def clean_hosts(assets):
continue continue
clean_assets.append(asset) clean_assets.append(asset)
if not clean_assets: if not clean_assets:
print(_("No assets matched, stop task")) logger.info(_("No assets matched, stop task"))
return clean_assets 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 @shared_task
def set_assets_hardware_info(assets, result, **kwargs): def set_assets_hardware_info(assets, result, **kwargs):
""" """
...@@ -96,7 +108,7 @@ def set_assets_hardware_info(assets, result, **kwargs): ...@@ -96,7 +108,7 @@ def set_assets_hardware_info(assets, result, **kwargs):
___disk_total = '%s %s' % sum_capacity(disk_info.values()) ___disk_total = '%s %s' % sum_capacity(disk_info.values())
___disk_info = json.dumps(disk_info) ___disk_info = json.dumps(disk_info)
___platform = info.get('ansible_system', 'Unknown') # ___platform = info.get('ansible_system', 'Unknown')
___os = info.get('ansible_distribution', 'Unknown') ___os = info.get('ansible_distribution', 'Unknown')
___os_version = info.get('ansible_distribution_version', 'Unknown') ___os_version = info.get('ansible_distribution_version', 'Unknown')
___os_arch = info.get('ansible_architecture', 'Unknown') ___os_arch = info.get('ansible_architecture', 'Unknown')
...@@ -163,25 +175,53 @@ def test_asset_connectivity_util(assets, task_name=None): ...@@ -163,25 +175,53 @@ def test_asset_connectivity_util(assets, task_name=None):
if task_name is None: if task_name is None:
task_name = _("Test assets connectivity") task_name = _("Test assets connectivity")
hosts = clean_hosts(assets) hosts = clean_hosts(assets)
if not hosts: if not hosts:
return {} return {}
tasks = const.TEST_ADMIN_USER_CONN_TASKS
created_by = assets[0].org_id hosts_category = {
task, created = update_or_create_ansible_task( 'linux': {
task_name=task_name, hosts=hosts, tasks=tasks, pattern='all', 'hosts': [],
options=const.TASK_OPTIONS, run_as_admin=True, created_by=created_by, '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
) )
result = task.run() created_by = assets[0].org_id
summary = result[1] for _, 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,
)
result = task.run()
summary = result[1]
results_summary['success'] &= summary['success']
results_summary['contacted'].update(summary['contacted'])
results_summary['dark'].update(summary['dark'])
for asset in assets: for asset in assets:
if asset.hostname in summary.get('dark', {}): if asset.hostname in results_summary.get('dark', {}):
asset.connectivity = asset.UNREACHABLE asset.connectivity = asset.UNREACHABLE
elif asset.hostname in summary.get('contacted', []): elif asset.hostname in results_summary.get('contacted', []):
asset.connectivity = asset.REACHABLE asset.connectivity = asset.REACHABLE
else: else:
asset.connectivity = asset.UNKNOWN asset.connectivity = asset.UNKNOWN
return summary
return results_summary
@shared_task @shared_task
...@@ -243,8 +283,7 @@ def test_admin_user_connectivity_manual(admin_user): ...@@ -243,8 +283,7 @@ def test_admin_user_connectivity_manual(admin_user):
## System user connective ## ## System user connective ##
@shared_task @shared_task
def set_system_user_connectivity_info(system_user, result): def set_system_user_connectivity_info(system_user, summary):
summary = result[1]
system_user.connectivity = summary system_user.connectivity = summary
...@@ -258,18 +297,50 @@ def test_system_user_connectivity_util(system_user, assets, task_name): ...@@ -258,18 +297,50 @@ def test_system_user_connectivity_util(system_user, assets, task_name):
:return: :return:
""" """
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
tasks = const.TEST_SYSTEM_USER_CONN_TASKS
hosts = clean_hosts(assets) hosts = clean_hosts(assets)
if not hosts: if not hosts:
return {} return {}
task, created = update_or_create_ansible_task(
task_name, hosts=hosts, tasks=tasks, pattern='all', hosts = clean_hosts_by_protocol(system_user, hosts)
options=const.TASK_OPTIONS, if not hosts:
run_as=system_user.username, created_by=system_user.org_id, 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
) )
result = task.run() for _, value in hosts_category.items():
set_system_user_connectivity_info(system_user, result) if not value['hosts']:
return result 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,
)
result = task.run()
summary = result[1]
results_summary['success'] &= summary['success']
results_summary['contacted'].update(summary['contacted'])
results_summary['dark'].update(summary['dark'])
set_system_user_connectivity_info(system_user, results_summary)
return results_summary
@shared_task @shared_task
...@@ -301,11 +372,7 @@ def test_system_user_connectivity_period(): ...@@ -301,11 +372,7 @@ def test_system_user_connectivity_period():
#### Push system user tasks #### #### Push system user tasks ####
def get_push_system_user_tasks(system_user): def get_push_linux_system_user_tasks(system_user):
# Set root as system user is dangerous
if system_user.username == "root":
return []
tasks = [] tasks = []
if system_user.password: if system_user.password:
tasks.append({ tasks.append({
...@@ -320,12 +387,12 @@ def get_push_system_user_tasks(system_user): ...@@ -320,12 +387,12 @@ def get_push_system_user_tasks(system_user):
}) })
tasks.extend([ tasks.extend([
{ {
'name': 'Check home dir exists', 'name': 'Check home dir exists',
'action': { 'action': {
'module': 'stat', 'module': 'stat',
'args': 'path=/home/{}'.format(system_user.username) 'args': 'path=/home/{}'.format(system_user.username)
}, },
'register': 'home_existed' 'register': 'home_existed'
}, },
{ {
'name': "Set home dir permission", 'name': "Set home dir permission",
...@@ -364,6 +431,46 @@ def get_push_system_user_tasks(system_user): ...@@ -364,6 +431,46 @@ def get_push_system_user_tasks(system_user):
) )
} }
}) })
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 return tasks
...@@ -372,16 +479,29 @@ def push_system_user_util(system_user, assets, task_name): ...@@ -372,16 +479,29 @@ def push_system_user_util(system_user, assets, task_name):
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
if not system_user.is_need_push(): if not system_user.is_need_push():
msg = _("Push system user task skip, auto push not enable or " msg = _("Push system user task skip, auto push not enable or "
"protocol is not ssh: {}").format(system_user.name) "protocol is not ssh or rdp: {}").format(system_user.name)
logger.info(msg) logger.info(msg)
return 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) hosts = clean_hosts(assets)
if not hosts: if not hosts:
return {} return {}
hosts = clean_hosts_by_protocol(system_user, hosts)
if not hosts:
return {}
for host in hosts: for host in hosts:
system_user.load_specific_asset_auth(host) system_user.load_specific_asset_auth(host)
tasks = get_push_system_user_tasks(system_user) tasks = get_push_system_user_tasks(host, system_user)
if not tasks:
continue
task, created = update_or_create_ansible_task( task, created = update_or_create_ansible_task(
task_name=task_name, hosts=[host], tasks=tasks, pattern='all', task_name=task_name, hosts=[host], tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, run_as_admin=True, options=const.TASK_OPTIONS, run_as_admin=True,
...@@ -423,6 +543,23 @@ def test_admin_user_connectability_period(): ...@@ -423,6 +543,23 @@ def test_admin_user_connectability_period():
pass 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 @shared_task
def set_asset_user_connectivity_info(asset_user, result): def set_asset_user_connectivity_info(asset_user, result):
summary = result[1] summary = result[1]
...@@ -437,10 +574,14 @@ def test_asset_user_connectivity_util(asset_user, task_name): ...@@ -437,10 +574,14 @@ def test_asset_user_connectivity_util(asset_user, task_name):
:return: :return:
""" """
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
tasks = const.TEST_ASSET_USER_CONN_TASKS
if not check_asset_can_run_ansible(asset_user.asset): if not check_asset_can_run_ansible(asset_user.asset):
return return
tasks = get_test_asset_user_connectivity_tasks(asset_user.asset)
if not tasks:
return
task, created = update_or_create_ansible_task( task, created = update_or_create_ansible_task(
task_name, hosts=[asset_user.asset], tasks=tasks, pattern='all', task_name, hosts=[asset_user.asset], tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, options=const.TASK_OPTIONS,
......
...@@ -95,41 +95,97 @@ var auto_push_id = '#' + '{{ form.auto_push.id_for_label }}'; ...@@ -95,41 +95,97 @@ var auto_push_id = '#' + '{{ form.auto_push.id_for_label }}';
var sudo_id = '#' + '{{ form.sudo.id_for_label }}'; var sudo_id = '#' + '{{ form.sudo.id_for_label }}';
var shell_id = '#' + '{{ form.shell.id_for_label }}'; var shell_id = '#' + '{{ form.shell.id_for_label }}';
var need_change_field = [ function autoLoginModeProtocol() {
auto_generate_key, private_key_id, auto_push_id, sudo_id, shell_id // 协议+自动登录模式字段控制
]; $('#auth_title_id').removeClass('hidden');
var need_change_field_login_mode = [
auto_generate_key, private_key_id, auto_push_id, password_id
];
function protocolChange() {
var protocol = $(protocol_id + " option:selected").text(); var protocol = $(protocol_id + " option:selected").text();
if (protocol === 'rdp' || protocol === 'vnc') { if (protocol === 'rdp') {
authFieldsDisplay();
$(auto_generate_key).closest('.form-group').removeClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').removeClass('hidden');
$(auto_push_id).closest('.form-group').removeClass('hidden');
$('#command-filter-block').addClass('hidden');
$(sudo_id).closest('.form-group').addClass('hidden');
$(shell_id).closest('.form-group').addClass('hidden');
}
else if (protocol === 'vnc') {
$('.auth-fields').removeClass('hidden'); $('.auth-fields').removeClass('hidden');
$(auto_generate_key).closest('.form-group').addClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').removeClass('hidden');
$(auto_push_id).closest('.form-group').addClass('hidden');
$('#command-filter-block').addClass('hidden'); $('#command-filter-block').addClass('hidden');
$.each(need_change_field, function (index, value) { $(sudo_id).closest('.form-group').addClass('hidden');
$(value).closest('.form-group').addClass('hidden') $(shell_id).closest('.form-group').addClass('hidden');
});
} }
else if (protocol === 'telnet (beta)') { else if (protocol === 'telnet (beta)') {
$('.auth-fields').removeClass('hidden'); $('.auth-fields').removeClass('hidden');
$(auto_generate_key).closest('.form-group').addClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').removeClass('hidden');
$(auto_push_id).closest('.form-group').addClass('hidden');
$('#command-filter-block').removeClass('hidden'); $('#command-filter-block').removeClass('hidden');
$.each(need_change_field, function (index, value) { $(sudo_id).closest('.form-group').addClass('hidden');
$(value).closest('.form-group').addClass('hidden') $(shell_id).closest('.form-group').addClass('hidden');
});
} }
else { else {
if($(login_mode_id).val() === 'manual'){
$(sudo_id).closest('.form-group').removeClass('hidden');
$(shell_id).closest('.form-group').removeClass('hidden');
return
}
authFieldsDisplay(); authFieldsDisplay();
$(auto_generate_key).closest('.form-group').removeClass('hidden');
$(private_key_id).closest('.form-group').removeClass('hidden');
$(password_id).closest('.form-group').removeClass('hidden');
$(auto_push_id).closest('.form-group').removeClass('hidden');
$('#command-filter-block').removeClass('hidden');
$(sudo_id).closest('.form-group').removeClass('hidden');
$(shell_id).closest('.form-group').removeClass('hidden');
}
}
function manualLoginModeProtocol() {
// 协议+手动登录模式字段控制
$('#auth_title_id').addClass('hidden');
var protocol = $(protocol_id + " option:selected").text();
if (protocol === 'rdp') {
$('.auth-fields').addClass('hidden');
$(auto_generate_key).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').addClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(auto_push_id).closest('.form-group').addClass('hidden');
$('#command-filter-block').addClass('hidden');
$(sudo_id).closest('.form-group').addClass('hidden');
$(shell_id).closest('.form-group').addClass('hidden');
}
else if (protocol === 'vnc') {
$('.auth-fields').addClass('hidden');
$(auto_generate_key).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').addClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(auto_push_id).closest('.form-group').addClass('hidden');
$('#command-filter-block').addClass('hidden');
$(sudo_id).closest('.form-group').addClass('hidden');
$(shell_id).closest('.form-group').addClass('hidden');
}
else if (protocol === 'telnet (beta)') {
$('.auth-fields').addClass('hidden');
$(auto_generate_key).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').addClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(auto_push_id).closest('.form-group').addClass('hidden');
$('#command-filter-block').removeClass('hidden'); $('#command-filter-block').removeClass('hidden');
$.each(need_change_field, function (index, value) { $(sudo_id).closest('.form-group').addClass('hidden');
$(value).closest('.form-group').removeClass('hidden') $(shell_id).closest('.form-group').addClass('hidden');
});
} }
else {
$('.auth-fields').addClass('hidden');
$(auto_generate_key).closest('.form-group').addClass('hidden');
$(password_id).closest('.form-group').addClass('hidden');
$(private_key_id).closest('.form-group').addClass('hidden');
$(auto_push_id).closest('.form-group').addClass('hidden');
$('#command-filter-block').removeClass('hidden');
$(sudo_id).closest('.form-group').removeClass('hidden');
$(shell_id).closest('.form-group').removeClass('hidden');
}
} }
function authFieldsDisplay() { function authFieldsDisplay() {
...@@ -139,34 +195,29 @@ function authFieldsDisplay() { ...@@ -139,34 +195,29 @@ function authFieldsDisplay() {
$('.auth-fields').removeClass('hidden'); $('.auth-fields').removeClass('hidden');
} }
} }
function loginModeChange(){ function fieldDisplay(){
if ($(login_mode_id).val() === 'manual'){ var login_mode = $(login_mode_id).val();
$('#auth_title_id').addClass('hidden'); if (login_mode === 'manual'){
$.each(need_change_field_login_mode, function(index, value){ manualLoginModeProtocol();
$(value).closest('.form-group').addClass('hidden')
})
} }
else if($(login_mode_id).val() === 'auto'){ else if(login_mode === 'auto'){
$('#auth_title_id').removeClass('hidden'); autoLoginModeProtocol();
$(password_id).closest('.form-group').removeClass('hidden')
protocolChange();
} }
} }
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
authFieldsDisplay(); authFieldsDisplay();
protocolChange(); fieldDisplay();
loginModeChange();
})
.on('change', protocol_id, function(){
protocolChange();
}) })
.on('change', auto_generate_key, function(){ .on('change', auto_generate_key, function(){
authFieldsDisplay(); authFieldsDisplay();
}) })
.on('change', login_mode_id, function(){ .on('change', login_mode_id, function(){
loginModeChange(); fieldDisplay();
})
.on('change', protocol_id, function(){
fieldDisplay();
}) })
</script> </script>
......
...@@ -62,7 +62,7 @@ ...@@ -62,7 +62,7 @@
<div class="panel-body"> <div class="panel-body">
<table class="table"> <table class="table">
<tbody> <tbody>
{% if asset.protocol == 'ssh' %} {% if asset.is_support_ansible %}
<tr class="no-borders-tr"> <tr class="no-borders-tr">
<td>{% trans 'Test connective' %}:</td> <td>{% trans 'Test connective' %}:</td>
<td> <td>
...@@ -118,7 +118,7 @@ function initAssetUserTable() { ...@@ -118,7 +118,7 @@ function initAssetUserTable() {
var view_btn = ' <a class="btn btn-xs btn-primary btn-view-auth" data-username="DEFAULT_USERNAME">{% trans "View auth" %}</a>'.replace("DEFAULT_USERNAME", cellData); var view_btn = ' <a class="btn btn-xs btn-primary btn-view-auth" data-username="DEFAULT_USERNAME">{% trans "View auth" %}</a>'.replace("DEFAULT_USERNAME", cellData);
var test_btn = ' <a class="btn btn-xs btn-info btn-test-connective" data-username="DEFAULT_USERNAME">{% trans "Test" %}</a>'.replace("DEFAULT_USERNAME", cellData); var test_btn = ' <a class="btn btn-xs btn-info btn-test-connective" data-username="DEFAULT_USERNAME">{% trans "Test" %}</a>'.replace("DEFAULT_USERNAME", cellData);
btn += view_btn; btn += view_btn;
{% if asset.protocol == 'ssh' %} {% if asset.is_support_ansible %}
btn += test_btn; btn += test_btn;
{% endif %} {% endif %}
$(td).html(btn); $(td).html(btn);
......
...@@ -16,12 +16,24 @@ ...@@ -16,12 +16,24 @@
<h3>{% trans 'Basic' %}</h3> <h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %} {% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %} {% bootstrap_field form.domain layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Protocols' %}</h3>
<div class="protocols">
{% for fm in formset.forms %}
<div class="form-group">
<div class="col-md-2 col-md-offset-2" style="text-align: right">{{ fm.name }}</div>
<div class="col-md-6">{{ fm.port }}</div>
<div class="col-md-1" style="padding: 6px 0">
<a class="btn btn-danger btn-xs btn-protocol btn-del"><span class="fa fa-minus"></span> </a>
<a class="btn btn-primary btn-xs btn-protocol btn-add" style="display: none"><span class="fa fa-plus"></span></a>
</div>
</div>
{% endfor %}
</div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3> <h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.admin_user layout="horizontal" %} {% bootstrap_field form.admin_user layout="horizontal" %}
...@@ -55,6 +67,8 @@ ...@@ -55,6 +67,8 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% block extra %}
{% endblock %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
...@@ -73,11 +87,25 @@ ...@@ -73,11 +87,25 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
var instanceId = "{{ object.id }}";
var protocolLen = 0;
function format(item) { function format(item) {
var group = item.element.parentElement.label; var group = item.element.parentElement.label;
return group + ':' + item.text; return group + ':' + item.text;
} }
function protocolBtnShow() {
$(".btn-protocol.btn-add").hide();
$(".btn-protocol.btn-add:last").show();
var btnDel = $(".btn-protocol.btn-del");
if (btnDel.length === 1) {
btnDel.addClass("disabled")
} else {
btnDel.removeClass("disabled")
}
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({ $('.select2').select2({
allowClear: true allowClear: true
...@@ -89,20 +117,133 @@ $(document).ready(function () { ...@@ -89,20 +117,133 @@ $(document).ready(function () {
$('#id_nodes.select2').select2({ $('#id_nodes.select2').select2({
closeOnSelect: false closeOnSelect: false
}); });
$("#id_protocol").change(function (){ protocolBtnShow()
var protocol = $("#id_protocol option:selected").text(); })
var port = 22; .on("change", "#id_platform", function () {
if(protocol === 'rdp'){ if (instanceId !== "") {
port = 3389; return
} }
else if(protocol === 'telnet (beta)'){ var platform = $(this).val();
port = 23; var protocolRef = $(".protocols").find("select").first()
var protocol = protocolRef.val();
var protocolShould = "";
if (platform.startsWith("Windows")){
protocolShould = "rdp"
} else {
protocolShould = "ssh"
}
if (protocol !== protocolShould) {
protocolRef.val(protocolShould);
protocolRef.trigger("change")
}
})
.on("click", ".btn-protocol.btn-del", function () {
$(this).parent().parent().remove();
protocolBtnShow()
})
.on("click", ".btn-protocol.btn-add", function () {
var protocol = "";
var protocolsRef = $(".protocols");
var firstProtocolForm = protocolsRef.children().first();
var newProtocolForm = firstProtocolForm.clone();
var protocolChoices = $.map($(firstProtocolForm.find('select option')), function (option) {
return option.value
});
var protocolsSet = $.map(protocolsRef.find('select option:selected'), function(option) {
return option.value
});
for (var i=0;i<protocolChoices.length;i++) {
var p = protocolChoices[i];
if (protocolsSet.indexOf(p) === -1) {
protocol = p;
break
} }
else if(protocol === 'vnc'){ }
if (protocol === "") {
return
}
if (protocolLen === 0) {
protocolLen = protocolsRef.length;
}
var selectName = "form-" + protocolLen + "-name";
var selectId = "id_" + selectName;
var portName = "form-" + protocolLen + "-port";
var portId = "id_" + portName;
newProtocolForm.find("select").prop("name", selectName).prop("id", selectId);
newProtocolForm.find("input").prop("name", portName).prop("id", portId);
newProtocolForm.find("option[value='" + protocol + "']").attr("selected", true);
protocolsRef.append(newProtocolForm);
protocolLen += 1;
$("#" + selectId).trigger("change");
protocolBtnShow()
})
.on("change", ".protocol-name", function () {
var name = $(this).val();
var port = 22;
switch (name) {
case "ssh":
port = 22;
break;
case "rdp":
port = 3389;
break;
case "telnet":
port = 21;
break;
case "vnc":
port = 5901; port = 5901;
} break;
$("#id_port").val(port); default:
}); port = 22;
break
}
$(this).parent().parent().find(".protocol-port").val(port);
}) })
</script> </script>
{% block form_submit %}
<script>
$(document).ready(function () {
})
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:asset-list' %}';
var redirect_to = '{% url "assets:asset-list" %}';
var form = $("form");
var protocols = {};
var data = form.serializeObject();
$.each(data, function (k, v) {
if (k.startsWith("form")){
delete data[k];
var _k = k.split("-");
var formName = _k.slice(0, 2).join("-");
var key = _k[_k.length-1];
if (!protocols[formName]) {
protocols[formName] = {}
}
protocols[formName][key] = v
}
});
protocols = $.map(protocols, function ( v) {
return v
});
data["protocols"] = protocols;
if (typeof data.labels === "string") {
data["labels"] = [data["labels"]];
}
if (typeof data["nodes"] == "string") {
data["nodes"] = [data["nodes"]]
}
var props = {
url: the_url,
data: data,
method: "POST",
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
</script>
{% endblock %}
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -69,12 +69,13 @@ ...@@ -69,12 +69,13 @@
<td><b>{{ asset.public_ip|default:"" }}</b></td> <td><b>{{ asset.public_ip|default:"" }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Port' %}:</td> <td>{% trans 'Protocol' %}</td>
<td><b>{{ asset.port }}</b></td> <td>
</tr> {% for protocol in asset.protocols.all %}
<tr> <b>{{ protocol.name }}:</b>
<td>{% trans 'Protocol' %}:</td> {{ protocol.port }}
<td><b>{{ asset.protocol }}</b></td> {% endfor %}
</td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Admin user' %}:</td> <td>{% trans 'Admin user' %}:</td>
...@@ -94,7 +95,7 @@ ...@@ -94,7 +95,7 @@
</tr> </tr>
<tr> <tr>
<td>{% trans 'CPU' %}:</td> <td>{% trans 'CPU' %}:</td>
<td><b>{{ asset.cpu_model|default:"" }} {{ asset.cpu_count|default:"" }}*{{ asset.cpu_cores|default:"" }}</b></td> <td><b>{{ asset.cpu_info }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Memory' %}:</td> <td>{% trans 'Memory' %}:</td>
...@@ -166,7 +167,7 @@ ...@@ -166,7 +167,7 @@
</span> </span>
</td> </td>
</tr> </tr>
{% if asset.protocol == 'ssh' %} {% if asset.is_support_ansible %}
<tr> <tr>
<td>{% trans 'Refresh hardware' %}:</td> <td>{% trans 'Refresh hardware' %}:</td>
<td> <td>
......
{% extends '_base_create_update.html' %} {% extends 'assets/asset_create.html' %}
{% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block custom_head_css_js_create %}
<link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/inputTags.jquery.min.js" %}"></script>
{% endblock %}
{% block form %}
<form action="" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
{% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.protocol layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Auth' %}</h3>
{% bootstrap_field form.admin_user layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Node' %}</h3>
{% bootstrap_field form.nodes layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Labels' %}</h3>
<div class="form-group">
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Label' %}</label>
<div class="col-md-9">
<select name="labels" class="select2 labels" data-placeholder="{% trans 'Label' %}" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
{% for name, labels in form.labels.field.queryset|group_labels %}
<optgroup label="{{ name }}">
{% for label in labels %}
{% if label in form.labels.initial %}
<option value="{{ label.id }}" selected>{{ label.value }}</option>
{% else %}
<option value="{{ label.id }}">{{ label.value }}</option>
{% endif %}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
</div>
{% block extra %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Configuration' %}</h3> <h3>{% trans 'Configuration' %}</h3>
{% bootstrap_field form.number layout="horizontal" %} {% bootstrap_field form.number layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block form_submit %}
<script> <script>
function format(item) {
var group = item.element.parentElement.label;
return group + ':' + item.text;
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({
allowClear: true
});
$(".labels").select2({
allowClear: true,
templateSelection: format
});
}) })
</script> .on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:asset-detail' pk=object.id %}';
var redirect_to = '{% url "assets:asset-list" %}';
var form = $("form");
var protocols = {};
var data = form.serializeObject();
$.each(data, function (k, v) {
if (k.startsWith("form")){
delete data[k];
var _k = k.split("-");
var formName = _k.slice(0, 2).join("-");
var key = _k[_k.length-1];
if (!protocols[formName]) {
protocols[formName] = {}
}
protocols[formName][key] = v
}
});
protocols = $.map(protocols, function ( v) {
return v
});
data["protocols"] = protocols;
if (typeof data["nodes"] == "string") {
data["nodes"] = [data["nodes"]]
}
var props = {
url: the_url,
data: data,
method: "PUT",
form: form,
redirect_to: redirect_to
};
formSubmit(props);
});
</script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
<div class="panel-body"> <div class="panel-body">
<table class="table"> <table class="table">
<tbody> <tbody>
<tr class="only-ssh"> <tr class="only-ssh-rdp">
<td width="50%">{% trans 'Auto push' %}:</td> <td width="50%">{% trans 'Auto push' %}:</td>
<td> <td>
<span class="pull-right"> <span class="pull-right">
...@@ -135,7 +135,7 @@ ...@@ -135,7 +135,7 @@
</td> </td>
</tr> </tr>
{% if system_user.auto_push %} {% if system_user.auto_push %}
<tr class="only-ssh"> <tr class="only-ssh-rdp">
<td width="50%">{% trans 'Push system user now' %}:</td> <td width="50%">{% trans 'Push system user now' %}:</td>
<td> <td>
<span style="float: right"> <span style="float: right">
...@@ -144,7 +144,7 @@ ...@@ -144,7 +144,7 @@
</td> </td>
</tr> </tr>
{% endif %} {% endif %}
<tr class="only-ssh"> <tr>
<td width="50%">{% trans 'Test assets connective' %}:</td> <td width="50%">{% trans 'Test assets connective' %}:</td>
<td> <td>
<span style="float: right"> <span style="float: right">
...@@ -219,8 +219,12 @@ function updateCommandFilters(command_filters) { ...@@ -219,8 +219,12 @@ function updateCommandFilters(command_filters) {
}); });
} }
$(document).ready(function () { $(document).ready(function () {
if($('#id_protocol_type').text() === 'rdp'){ var protocol = $('#id_protocol_type').text();
$('.only-ssh').addClass('hidden') if(protocol !== 'ssh'){
$('.only-ssh').addClass("hidden")
}
if(["ssh", "rdp"].indexOf(protocol) === -1){
$('.only-ssh-rdp').addClass('hidden');
} }
$(".panel-body .table tr:visible:first").addClass('no-borders-tr'); $(".panel-body .table tr:visible:first").addClass('no-borders-tr');
$('.select2').select2() $('.select2').select2()
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
{# 目前还不支持Windows的自动推送#} {# 目前还不支持Windows的自动推送#}
{% trans 'System user is Jumpserver jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); '%} {% trans 'System user is Jumpserver jump login assets used by the users, can be understood as the user login assets, such as web, sa, the dba (` ssh web@some-host `), rather than using a user the username login server jump (` ssh xiaoming@some-host `); '%}
{% trans 'In simple terms, users log into Jumpserver using their own username, and Jumpserver uses system users to log into assets. '%} {% trans 'In simple terms, users log into Jumpserver using their own username, and Jumpserver uses system users to log into assets. '%}
{% trans 'When system users are created, if you choose auto push Jumpserver to use ansible push system users into the asset, if the asset (Switch, Windows) does not support ansible, please manually fill in the account password. Automatic push for Windows is not currently supported.' %} {% trans 'When system users are created, if you choose auto push Jumpserver to use ansible push system users into the asset, if the asset (Switch) does not support ansible, please manually fill in the account password.' %}
</div> </div>
{% endblock %} {% endblock %}
......
...@@ -23,6 +23,7 @@ from django.utils import timezone ...@@ -23,6 +23,7 @@ from django.utils import timezone
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect from django.shortcuts import redirect
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.forms.formsets import formset_factory
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger from common.utils import get_object_or_none, get_logger
...@@ -30,8 +31,6 @@ from common.permissions import AdminUserRequiredMixin ...@@ -30,8 +31,6 @@ from common.permissions import AdminUserRequiredMixin
from common.const import ( from common.const import (
create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID create_success_msg, update_success_msg, KEY_CACHE_RESOURCES_ID
) )
from ..const import CACHE_KEY_ASSET_BULK_UPDATE_ID_PREFIX
from orgs.utils import current_org
from .. import forms from .. import forms
from ..models import Asset, AdminUser, SystemUser, Label, Node, Domain from ..models import Asset, AdminUser, SystemUser, Label, Node, Domain
...@@ -102,10 +101,30 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): ...@@ -102,10 +101,30 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
form["nodes"].initial = node form["nodes"].initial = node
return form return form
def get_protocol_formset(self):
ProtocolFormset = formset_factory(forms.ProtocolForm, extra=0, min_num=1, max_num=5)
if self.request.method == "POST":
formset = ProtocolFormset(self.request.POST)
else:
formset = ProtocolFormset()
return formset
def form_valid(self, form):
formset = self.get_protocol_formset()
valid = formset.is_valid()
if not valid:
return self.form_invalid(form)
protocols = formset.save()
instance = super().form_valid(form)
instance.protocols.set(protocols)
return instance
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
formset = self.get_protocol_formset()
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create asset'), 'action': _('Create asset'),
'formset': formset,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -160,10 +179,21 @@ class AssetUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -160,10 +179,21 @@ class AssetUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
template_name = 'assets/asset_update.html' template_name = 'assets/asset_update.html'
success_url = reverse_lazy('assets:asset-list') success_url = reverse_lazy('assets:asset-list')
def get_protocol_formset(self):
ProtocolFormset = formset_factory(forms.ProtocolForm, extra=0, min_num=1, max_num=5)
if self.request.method == "POST":
formset = ProtocolFormset(self.request.POST)
else:
initial_data = [{"name": p.name, "port": p.port} for p in self.object.protocols.all()]
formset = ProtocolFormset(initial=initial_data)
return formset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
formset = self.get_protocol_formset()
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update asset'), 'action': _('Update asset'),
'formset': formset,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
# -*- coding: utf-8 -*-
#
from django.db import transaction
def on_transaction_commit(func):
"""
如果不调用on_commit, 对象创建时添加多对多字段值失败
"""
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
...@@ -5,6 +5,7 @@ from django.http import JsonResponse ...@@ -5,6 +5,7 @@ from django.http import JsonResponse
from django.utils import timezone from django.utils import timezone
from django.core.cache import cache from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from rest_framework.utils import html from rest_framework.utils import html
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
...@@ -203,3 +204,26 @@ class DatetimeSearchMixin: ...@@ -203,3 +204,26 @@ class DatetimeSearchMixin:
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.get_date_range() self.get_date_range()
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
class ApiMessageMixin:
success_message = _("%(name)s was %(action)s successfully")
_action_map = {"create": _("create"), "update": _("update")}
def get_success_message(self, cleaned_data):
data = {k: v for k, v in cleaned_data.items()}
action = getattr(self, "action", "create")
data["action"] = self._action_map.get(action)
message = self.success_message % data
return message
def dispatch(self, request, *args, **kwargs):
resp = super().dispatch(request, *args, **kwargs)
if request.method.lower() in ("get", "delete"):
return resp
if resp.status_code >= 400:
return resp
message = self.get_success_message(resp.data)
if message:
messages.success(request, message)
return resp
...@@ -3,5 +3,22 @@ ...@@ -3,5 +3,22 @@
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework.validators import (
UniqueTogetherValidator, ValidationError
)
alphanumeric = RegexValidator(r'^[0-9a-zA-Z_@\-\.]*$', _('Special char not allowed'))
\ No newline at end of file alphanumeric = RegexValidator(r'^[0-9a-zA-Z_@\-\.]*$', _('Special char not allowed'))
class ProjectUniqueValidator(UniqueTogetherValidator):
def __call__(self, attrs):
try:
super().__call__(attrs)
except ValidationError as e:
errors = {}
for field in self.fields:
if field == "org_id":
continue
errors[field] = _('This field must be unique.')
raise ValidationError(errors)
...@@ -287,7 +287,10 @@ class Config(dict): ...@@ -287,7 +287,10 @@ class Config(dict):
return v return v
try: try:
v = tp(v) if tp in [list, dict]:
v = json.loads(v)
else:
v = tp(v)
except Exception: except Exception:
pass pass
return v return v
......
...@@ -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-06-12 17:56+0800\n" "POT-Creation-Date: 2019-06-13 18:56+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"
...@@ -76,7 +76,7 @@ msgstr "运行参数" ...@@ -76,7 +76,7 @@ msgstr "运行参数"
#: 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:279 assets/models/authbook.py:27 #: assets/models/asset.py:320 assets/models/authbook.py:27
#: assets/serializers/admin_user.py:23 assets/serializers/system_user.py:28 #: assets/serializers/admin_user.py:23 assets/serializers/system_user.py:28
#: assets/templates/assets/admin_user_list.html:49 #: assets/templates/assets/admin_user_list.html:49
#: assets/templates/assets/domain_detail.html:60 #: assets/templates/assets/domain_detail.html:60
...@@ -95,7 +95,7 @@ msgstr "运行参数" ...@@ -95,7 +95,7 @@ msgstr "运行参数"
#: terminal/templates/terminal/session_list.html:41 #: terminal/templates/terminal/session_list.html:41
#: terminal/templates/terminal/session_list.html:72 #: terminal/templates/terminal/session_list.html:72
#: xpack/plugins/change_auth_plan/forms.py:114 #: xpack/plugins/change_auth_plan/forms.py:114
#: xpack/plugins/change_auth_plan/models.py:409 #: xpack/plugins/change_auth_plan/models.py:413
#: 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
...@@ -132,11 +132,11 @@ msgstr "系统用户" ...@@ -132,11 +132,11 @@ msgstr "系统用户"
#: 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
#: applications/templates/applications/user_remote_app_list.html:16 #: applications/templates/applications/user_remote_app_list.html:16
#: assets/forms/domain.py:73 assets/forms/user.py:84 assets/forms/user.py:146 #: assets/forms/domain.py:73 assets/forms/user.py:84 assets/forms/user.py:148
#: assets/models/base.py:26 assets/models/cluster.py:18 #: assets/models/asset.py:72 assets/models/base.py:26
#: assets/models/cmd_filter.py:20 assets/models/domain.py:20 #: assets/models/cluster.py:18 assets/models/cmd_filter.py:20
#: assets/models/group.py:20 assets/models/label.py:18 #: assets/models/domain.py:20 assets/models/group.py:20
#: assets/templates/assets/admin_user_detail.html:56 #: assets/models/label.py:18 assets/templates/assets/admin_user_detail.html:56
#: assets/templates/assets/admin_user_list.html:47 #: assets/templates/assets/admin_user_list.html:47
#: assets/templates/assets/cmd_filter_detail.html:61 #: assets/templates/assets/cmd_filter_detail.html:61
#: assets/templates/assets/cmd_filter_list.html:24 #: assets/templates/assets/cmd_filter_list.html:24
...@@ -173,7 +173,7 @@ msgstr "系统用户" ...@@ -173,7 +173,7 @@ msgstr "系统用户"
#: users/templates/users/user_profile.html:51 #: users/templates/users/user_profile.html:51
#: users/templates/users/user_pubkey_update.html:53 #: users/templates/users/user_pubkey_update.html:53
#: xpack/plugins/change_auth_plan/forms.py:97 #: xpack/plugins/change_auth_plan/forms.py:97
#: xpack/plugins/change_auth_plan/models.py:58 #: xpack/plugins/change_auth_plan/models.py:61
#: 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:49 xpack/plugins/cloud/models.py:119 #: xpack/plugins/cloud/models.py:49 xpack/plugins/cloud/models.py:119
...@@ -204,11 +204,11 @@ msgstr "参数" ...@@ -204,11 +204,11 @@ msgstr "参数"
#: applications/models/remote_app.py:43 #: applications/models/remote_app.py:43
#: applications/templates/applications/remote_app_detail.html:77 #: applications/templates/applications/remote_app_detail.html:77
#: assets/models/asset.py:109 assets/models/base.py:34 #: assets/models/asset.py:132 assets/models/base.py:34
#: 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
#: assets/templates/assets/admin_user_detail.html:68 #: assets/templates/assets/admin_user_detail.html:68
#: assets/templates/assets/asset_detail.html:128 #: assets/templates/assets/asset_detail.html:129
#: 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
...@@ -218,7 +218,7 @@ msgstr "参数" ...@@ -218,7 +218,7 @@ msgstr "参数"
#: perms/templates/perms/remote_app_permission_detail.html:90 #: perms/templates/perms/remote_app_permission_detail.html:90
#: users/models/user.py:102 users/serializers/v1.py:72 #: users/models/user.py:102 users/serializers/v1.py:72
#: users/templates/users/user_detail.html:111 #: users/templates/users/user_detail.html:111
#: xpack/plugins/change_auth_plan/models.py:103 #: xpack/plugins/change_auth_plan/models.py:106
#: 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:55 xpack/plugins/cloud/models.py:127 #: xpack/plugins/cloud/models.py:55 xpack/plugins/cloud/models.py:127
msgid "Created by" msgid "Created by"
...@@ -228,7 +228,7 @@ msgstr "创建者" ...@@ -228,7 +228,7 @@ msgstr "创建者"
# msgstr "创建者" # msgstr "创建者"
#: applications/models/remote_app.py:46 #: applications/models/remote_app.py:46
#: applications/templates/applications/remote_app_detail.html:73 #: applications/templates/applications/remote_app_detail.html:73
#: assets/models/asset.py:110 assets/models/cluster.py:26 #: assets/models/asset.py:133 assets/models/cluster.py:26
#: assets/models/domain.py:23 assets/models/group.py:22 #: assets/models/domain.py:23 assets/models/group.py:22
#: assets/models/label.py:25 assets/serializers/admin_user.py:37 #: assets/models/label.py:25 assets/serializers/admin_user.py:37
#: assets/templates/assets/admin_user_detail.html:64 #: assets/templates/assets/admin_user_detail.html:64
...@@ -256,13 +256,13 @@ msgstr "创建日期" ...@@ -256,13 +256,13 @@ msgstr "创建日期"
#: applications/templates/applications/remote_app_detail.html:81 #: applications/templates/applications/remote_app_detail.html:81
#: applications/templates/applications/remote_app_list.html:24 #: applications/templates/applications/remote_app_list.html:24
#: applications/templates/applications/user_remote_app_list.html:20 #: applications/templates/applications/user_remote_app_list.html:20
#: assets/models/asset.py:111 assets/models/base.py:31 #: assets/models/asset.py:134 assets/models/base.py:31
#: 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
#: assets/models/domain.py:53 assets/models/group.py:23 #: assets/models/domain.py:53 assets/models/group.py:23
#: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:72 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:72
#: assets/templates/assets/admin_user_list.html:53 #: assets/templates/assets/admin_user_list.html:53
#: assets/templates/assets/asset_detail.html:136 #: assets/templates/assets/asset_detail.html:137
#: assets/templates/assets/cmd_filter_detail.html:65 #: assets/templates/assets/cmd_filter_detail.html:65
#: assets/templates/assets/cmd_filter_list.html:27 #: assets/templates/assets/cmd_filter_list.html:27
#: assets/templates/assets/cmd_filter_rule_list.html:62 #: assets/templates/assets/cmd_filter_rule_list.html:62
...@@ -282,7 +282,7 @@ msgstr "创建日期" ...@@ -282,7 +282,7 @@ msgstr "创建日期"
#: 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:134 #: users/templates/users/user_profile.html:134
#: xpack/plugins/change_auth_plan/models.py:99 #: xpack/plugins/change_auth_plan/models.py:102
#: 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:54 xpack/plugins/cloud/models.py:125 #: xpack/plugins/cloud/models.py:54 xpack/plugins/cloud/models.py:125
...@@ -310,8 +310,7 @@ msgstr "远程应用" ...@@ -310,8 +310,7 @@ msgstr "远程应用"
#: 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
#: assets/templates/assets/asset_create.html:67 #: assets/templates/assets/asset_create.html:81
#: assets/templates/assets/asset_update.html:71
#: assets/templates/assets/cmd_filter_create_update.html:15 #: assets/templates/assets/cmd_filter_create_update.html:15
#: assets/templates/assets/cmd_filter_rule_create_update.html:40 #: assets/templates/assets/cmd_filter_rule_create_update.html:40
#: assets/templates/assets/domain_create_update.html:16 #: assets/templates/assets/domain_create_update.html:16
...@@ -347,9 +346,8 @@ msgstr "重置" ...@@ -347,9 +346,8 @@ msgstr "重置"
#: 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
#: assets/templates/assets/asset_create.html:68 #: assets/templates/assets/asset_create.html:82
#: assets/templates/assets/asset_list.html:125 #: assets/templates/assets/asset_list.html:125
#: assets/templates/assets/asset_update.html:72
#: assets/templates/assets/cmd_filter_create_update.html:16 #: assets/templates/assets/cmd_filter_create_update.html:16
#: assets/templates/assets/cmd_filter_rule_create_update.html:41 #: assets/templates/assets/cmd_filter_rule_create_update.html:41
#: assets/templates/assets/domain_create_update.html:17 #: assets/templates/assets/domain_create_update.html:17
...@@ -562,9 +560,9 @@ msgstr "连接" ...@@ -562,9 +560,9 @@ msgstr "连接"
#: assets/templates/assets/system_user_detail.html:22 #: assets/templates/assets/system_user_detail.html:22
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47 #: assets/views/admin_user.py:29 assets/views/admin_user.py:47
#: assets/views/admin_user.py:63 assets/views/admin_user.py:78 #: assets/views/admin_user.py:63 assets/views/admin_user.py:78
#: assets/views/admin_user.py:102 assets/views/asset.py:53 #: assets/views/admin_user.py:102 assets/views/asset.py:52
#: assets/views/asset.py:69 assets/views/asset.py:107 assets/views/asset.py:148 #: assets/views/asset.py:68 assets/views/asset.py:125 assets/views/asset.py:167
#: assets/views/asset.py:165 assets/views/asset.py:189 #: assets/views/asset.py:194 assets/views/asset.py:219
#: assets/views/cmd_filter.py:30 assets/views/cmd_filter.py:46 #: assets/views/cmd_filter.py:30 assets/views/cmd_filter.py:46
#: assets/views/cmd_filter.py:62 assets/views/cmd_filter.py:78 #: assets/views/cmd_filter.py:62 assets/views/cmd_filter.py:78
#: assets/views/cmd_filter.py:97 assets/views/cmd_filter.py:130 #: assets/views/cmd_filter.py:97 assets/views/cmd_filter.py:130
...@@ -575,7 +573,7 @@ msgstr "连接" ...@@ -575,7 +573,7 @@ msgstr "连接"
#: assets/views/label.py:26 assets/views/label.py:43 assets/views/label.py:69 #: assets/views/label.py:26 assets/views/label.py:43 assets/views/label.py:69
#: assets/views/system_user.py:28 assets/views/system_user.py:44 #: assets/views/system_user.py:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:74 #: assets/views/system_user.py:60 assets/views/system_user.py:74
#: templates/_nav.html:19 xpack/plugins/change_auth_plan/models.py:65 #: templates/_nav.html:19 xpack/plugins/change_auth_plan/models.py:68
msgid "Assets" msgid "Assets"
msgstr "资产管理" msgstr "资产管理"
...@@ -595,7 +593,12 @@ msgstr "远程应用详情" ...@@ -595,7 +593,12 @@ msgstr "远程应用详情"
msgid "My RemoteApp" msgid "My RemoteApp"
msgstr "我的远程应用" msgstr "我的远程应用"
#: assets/api/asset.py:128 #: assets/api/asset.py:50
#, python-format
msgid "%(hostname)s was %(action)s successfully"
msgstr "%(hostname)s %(action)s成功"
#: assets/api/asset.py:129
msgid "Please select assets that need to be updated" msgid "Please select assets that need to be updated"
msgstr "请选择需要更新的资产" msgstr "请选择需要更新的资产"
...@@ -611,47 +614,44 @@ msgstr "更新节点资产硬件信息: {}" ...@@ -611,47 +614,44 @@ msgstr "更新节点资产硬件信息: {}"
msgid "Test if the assets under the node are connectable: {}" msgid "Test if the assets under the node are connectable: {}"
msgstr "测试节点下资产是否可连接: {}" msgstr "测试节点下资产是否可连接: {}"
#: assets/forms/asset.py:27 assets/models/asset.py:80 assets/models/user.py:133 #: assets/forms/asset.py:45 assets/models/asset.py:103
#: assets/templates/assets/asset_detail.html:194 #: assets/models/user.py:133 assets/templates/assets/asset_detail.html:195
#: assets/templates/assets/asset_detail.html:202 #: assets/templates/assets/asset_detail.html:203
#: assets/templates/assets/system_user_asset.html:95 #: assets/templates/assets/system_user_asset.html:95
#: perms/models/asset_permission.py:38 #: perms/models/asset_permission.py:38
#: xpack/plugins/change_auth_plan/models.py:69 #: xpack/plugins/change_auth_plan/models.py:72
msgid "Nodes" msgid "Nodes"
msgstr "节点管理" msgstr "节点管理"
#: assets/forms/asset.py:30 assets/forms/asset.py:66 assets/models/asset.py:84 #: assets/forms/asset.py:48 assets/forms/asset.py:83 assets/models/asset.py:107
#: assets/models/cluster.py:19 assets/models/user.py:91 #: assets/models/cluster.py:19 assets/models/user.py:91
#: assets/templates/assets/asset_detail.html:80 templates/_nav.html:24 #: assets/templates/assets/asset_detail.html:81 templates/_nav.html:24
#: xpack/plugins/cloud/models.py:124 #: xpack/plugins/cloud/models.py:124
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:65 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:65
#: xpack/plugins/orgs/templates/orgs/org_list.html:18 #: xpack/plugins/orgs/templates/orgs/org_list.html:18
msgid "Admin user" msgid "Admin user"
msgstr "管理用户" msgstr "管理用户"
#: assets/forms/asset.py:33 assets/forms/asset.py:69 assets/forms/asset.py:109 #: assets/forms/asset.py:51 assets/forms/asset.py:86 assets/forms/asset.py:125
#: assets/templates/assets/asset_create.html:36 #: assets/templates/assets/asset_create.html:48
#: assets/templates/assets/asset_create.html:38 #: assets/templates/assets/asset_create.html:50
#: assets/templates/assets/asset_list.html:93 #: assets/templates/assets/asset_list.html:93
#: assets/templates/assets/asset_update.html:41
#: assets/templates/assets/asset_update.html:43
#: assets/templates/assets/user_asset_list.html:33 #: assets/templates/assets/user_asset_list.html:33
#: xpack/plugins/orgs/templates/orgs/org_list.html:20 #: xpack/plugins/orgs/templates/orgs/org_list.html:20
msgid "Label" msgid "Label"
msgstr "标签" msgstr "标签"
#: assets/forms/asset.py:37 assets/forms/asset.py:73 assets/models/asset.py:79 #: assets/forms/asset.py:54 assets/forms/asset.py:89 assets/models/asset.py:102
#: assets/models/domain.py:26 assets/models/domain.py:52 #: assets/models/domain.py:26 assets/models/domain.py:52
#: assets/templates/assets/asset_detail.html:84 #: assets/templates/assets/asset_detail.html:85
#: assets/templates/assets/user_asset_list.html:173 #: assets/templates/assets/user_asset_list.html:173
#: xpack/plugins/orgs/templates/orgs/org_list.html:17 #: xpack/plugins/orgs/templates/orgs/org_list.html:17
msgid "Domain" msgid "Domain"
msgstr "网域" msgstr "网域"
#: assets/forms/asset.py:41 assets/forms/asset.py:63 assets/forms/asset.py:77 #: assets/forms/asset.py:58 assets/forms/asset.py:80 assets/forms/asset.py:93
#: assets/forms/asset.py:112 assets/models/node.py:31 #: assets/forms/asset.py:128 assets/models/node.py:31
#: assets/templates/assets/asset_create.html:30 #: assets/templates/assets/asset_create.html:42
#: assets/templates/assets/asset_update.html:35
#: perms/forms/asset_permission.py:49 perms/forms/asset_permission.py:59 #: perms/forms/asset_permission.py:49 perms/forms/asset_permission.py:59
#: perms/models/asset_permission.py:57 #: perms/models/asset_permission.py:57
#: perms/templates/perms/asset_permission_list.html:57 #: perms/templates/perms/asset_permission_list.html:57
...@@ -666,7 +666,7 @@ msgstr "网域" ...@@ -666,7 +666,7 @@ msgstr "网域"
msgid "Node" msgid "Node"
msgstr "节点" msgstr "节点"
#: assets/forms/asset.py:45 assets/forms/asset.py:81 #: assets/forms/asset.py:62 assets/forms/asset.py:97
msgid "" msgid ""
"root or other NOPASSWD sudo privilege user existed in asset,If asset is " "root or other NOPASSWD sudo privilege user existed in asset,If asset is "
"windows or other set any one, more see admin user left menu" "windows or other set any one, more see admin user left menu"
...@@ -674,17 +674,17 @@ msgstr "" ...@@ -674,17 +674,17 @@ msgstr ""
"root或其他拥有NOPASSWD: ALL权限的用户, 如果是windows或其它硬件可以随意设置一" "root或其他拥有NOPASSWD: ALL权限的用户, 如果是windows或其它硬件可以随意设置一"
"个, 更多信息查看左侧 `管理用户` 菜单" "个, 更多信息查看左侧 `管理用户` 菜单"
#: assets/forms/asset.py:48 assets/forms/asset.py:84 #: assets/forms/asset.py:65 assets/forms/asset.py:100
msgid "Windows 2016 RDP protocol is different, If is window 2016, set it" msgid "Windows 2016 RDP protocol is different, If is window 2016, set it"
msgstr "Windows 2016的RDP协议与之前不同,如果是请设置" msgstr "Windows 2016的RDP协议与之前不同,如果是请设置"
#: assets/forms/asset.py:49 assets/forms/asset.py:85 #: assets/forms/asset.py:66 assets/forms/asset.py:101
msgid "" msgid ""
"If your have some network not connect with each other, you can set domain" "If your have some network not connect with each other, you can set domain"
msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,使用网域网关跳转登录" msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,使用网域网关跳转登录"
#: assets/forms/asset.py:92 assets/forms/asset.py:96 assets/forms/domain.py:17 #: assets/forms/asset.py:108 assets/forms/asset.py:112
#: assets/forms/label.py:15 #: assets/forms/domain.py:17 assets/forms/label.py:15
#: perms/templates/perms/asset_permission_asset.html:88 #: perms/templates/perms/asset_permission_asset.html:88
#: xpack/plugins/change_auth_plan/forms.py:105 #: xpack/plugins/change_auth_plan/forms.py:105
#: 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:84
...@@ -699,7 +699,7 @@ msgstr "不能包含特殊字符" ...@@ -699,7 +699,7 @@ msgstr "不能包含特殊字符"
msgid "SSH gateway support proxy SSH,RDP,VNC" 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:85 assets/forms/user.py:147 #: assets/forms/domain.py:74 assets/forms/user.py:85 assets/forms/user.py:149
#: assets/models/base.py:27 #: assets/models/base.py:27
#: assets/templates/assets/_asset_user_auth_modal.html:15 #: assets/templates/assets/_asset_user_auth_modal.html:15
#: assets/templates/assets/_asset_user_view_auth_modal.html:31 #: assets/templates/assets/_asset_user_view_auth_modal.html:31
...@@ -721,8 +721,8 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC" ...@@ -721,8 +721,8 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: 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:99 #: xpack/plugins/change_auth_plan/forms.py:99
#: xpack/plugins/change_auth_plan/models.py:60 #: xpack/plugins/change_auth_plan/models.py:63
#: xpack/plugins/change_auth_plan/models.py:405 #: xpack/plugins/change_auth_plan/models.py:409
#: 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
...@@ -749,8 +749,8 @@ msgstr "密码或密钥密码" ...@@ -749,8 +749,8 @@ msgstr "密码或密钥密码"
#: users/templates/users/user_profile_update.html:40 #: users/templates/users/user_profile_update.html:40
#: users/templates/users/user_pubkey_update.html:40 #: users/templates/users/user_pubkey_update.html:40
#: users/templates/users/user_update.html:20 #: users/templates/users/user_update.html:20
#: xpack/plugins/change_auth_plan/models.py:90 #: xpack/plugins/change_auth_plan/models.py:93
#: xpack/plugins/change_auth_plan/models.py:260 #: xpack/plugins/change_auth_plan/models.py:264
msgid "Password" msgid "Password"
msgstr "密码" msgstr "密码"
...@@ -767,21 +767,21 @@ msgstr "不合法的密钥,仅支持RSA/DSA格式的密钥" ...@@ -767,21 +767,21 @@ msgstr "不合法的密钥,仅支持RSA/DSA格式的密钥"
msgid "Password and private key file must be input one" msgid "Password and private key file must be input one"
msgstr "密码和私钥, 必须输入一个" msgstr "密码和私钥, 必须输入一个"
#: assets/forms/user.py:134 #: assets/forms/user.py:136
msgid "* Automatic login mode must fill in the username." msgid "* Automatic login mode must fill in the username."
msgstr "自动登录模式,必须填写用户名" msgstr "自动登录模式,必须填写用户名"
#: assets/forms/user.py:149 assets/models/cmd_filter.py:31 #: assets/forms/user.py:151 assets/models/cmd_filter.py:31
#: assets/models/user.py:141 assets/templates/assets/_system_user.html:66 #: assets/models/user.py:141 assets/templates/assets/_system_user.html:66
#: assets/templates/assets/system_user_detail.html:165 #: assets/templates/assets/system_user_detail.html:165
msgid "Command filter" msgid "Command filter"
msgstr "命令过滤器" msgstr "命令过滤器"
#: assets/forms/user.py:153 #: assets/forms/user.py:155
msgid "Auto push system user to asset" msgid "Auto push system user to asset"
msgstr "自动推送系统用户到资产" msgstr "自动推送系统用户到资产"
#: assets/forms/user.py:154 #: assets/forms/user.py:156
msgid "" msgid ""
"1-100, High level will be using login asset as default, if user was granted " "1-100, High level will be using login asset as default, if user was granted "
"more than 2 system user" "more than 2 system user"
...@@ -789,17 +789,26 @@ msgstr "" ...@@ -789,17 +789,26 @@ msgstr ""
"1-100, 1最低优先级,100最高优先级。授权多个用户时,高优先级的系统用户将会作为" "1-100, 1最低优先级,100最高优先级。授权多个用户时,高优先级的系统用户将会作为"
"默认登录用户" "默认登录用户"
#: assets/forms/user.py:156 #: assets/forms/user.py:158
msgid "" msgid ""
"If you choose manual login mode, you do not need to fill in the username and " "If you choose manual login mode, you do not need to fill in the username and "
"password." "password."
msgstr "如果选择手动登录模式,用户名和密码可以不填写" msgstr "如果选择手动登录模式,用户名和密码可以不填写"
#: assets/forms/user.py:158 #: assets/forms/user.py:160
msgid "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig" msgid "Use comma split multi command, ex: /bin/whoami,/bin/ifconfig"
msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
#: assets/models/asset.py:74 assets/models/domain.py:49 #: assets/models/asset.py:73 assets/models/asset.py:98
#: assets/models/domain.py:50 assets/templates/assets/admin_user_assets.html:50
#: assets/templates/assets/domain_gateway_list.html:69
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/user_asset_list.html:168
#: settings/templates/settings/replay_storage_create.html:59
msgid "Port"
msgstr "端口"
#: assets/models/asset.py:93 assets/models/domain.py:49
#: assets/templates/assets/_asset_list_modal.html:46 #: assets/templates/assets/_asset_list_modal.html:46
#: assets/templates/assets/admin_user_assets.html:49 #: assets/templates/assets/admin_user_assets.html:49
#: assets/templates/assets/asset_detail.html:64 #: assets/templates/assets/asset_detail.html:64
...@@ -816,7 +825,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig" ...@@ -816,7 +825,7 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
msgid "IP" msgid "IP"
msgstr "IP" msgstr "IP"
#: assets/models/asset.py:75 assets/templates/assets/_asset_list_modal.html:45 #: assets/models/asset.py:94 assets/templates/assets/_asset_list_modal.html:45
#: assets/templates/assets/_asset_user_auth_modal.html:9 #: assets/templates/assets/_asset_user_auth_modal.html:9
#: assets/templates/assets/_asset_user_view_auth_modal.html:25 #: assets/templates/assets/_asset_user_view_auth_modal.html:25
#: assets/templates/assets/admin_user_assets.html:48 #: assets/templates/assets/admin_user_assets.html:48
...@@ -833,8 +842,9 @@ msgstr "IP" ...@@ -833,8 +842,9 @@ msgstr "IP"
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: assets/models/asset.py:76 assets/models/domain.py:51 #: assets/models/asset.py:97 assets/models/asset.py:100
#: assets/models/user.py:136 assets/templates/assets/asset_detail.html:76 #: assets/models/domain.py:51 assets/models/user.py:136
#: assets/templates/assets/asset_detail.html:72
#: assets/templates/assets/domain_gateway_list.html:70 #: assets/templates/assets/domain_gateway_list.html:70
#: assets/templates/assets/system_user_detail.html:70 #: assets/templates/assets/system_user_detail.html:70
#: assets/templates/assets/system_user_list.html:53 #: assets/templates/assets/system_user_list.html:53
...@@ -843,108 +853,97 @@ msgstr "主机名" ...@@ -843,108 +853,97 @@ msgstr "主机名"
msgid "Protocol" msgid "Protocol"
msgstr "协议" msgstr "协议"
#: assets/models/asset.py:77 assets/models/domain.py:50 #: assets/models/asset.py:101 assets/templates/assets/asset_detail.html:109
#: assets/templates/assets/admin_user_assets.html:50
#: assets/templates/assets/asset_detail.html:72
#: assets/templates/assets/domain_gateway_list.html:69
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/user_asset_list.html:168
#: settings/templates/settings/replay_storage_create.html:59
msgid "Port"
msgstr "端口"
#: assets/models/asset.py:78 assets/templates/assets/asset_detail.html:108
#: assets/templates/assets/user_asset_list.html:170 #: assets/templates/assets/user_asset_list.html:170
msgid "Platform" msgid "Platform"
msgstr "系统平台" msgstr "系统平台"
#: assets/models/asset.py:81 assets/models/cmd_filter.py:21 #: assets/models/asset.py:104 assets/models/cmd_filter.py:21
#: assets/models/domain.py:54 assets/models/label.py:22 #: assets/models/domain.py:54 assets/models/label.py:22
#: assets/templates/assets/asset_detail.html:116 #: assets/templates/assets/asset_detail.html:117
#: assets/templates/assets/user_asset_list.html:174 #: assets/templates/assets/user_asset_list.html:174
msgid "Is active" msgid "Is active"
msgstr "激活" msgstr "激活"
#: assets/models/asset.py:87 assets/templates/assets/asset_detail.html:68 #: assets/models/asset.py:110 assets/templates/assets/asset_detail.html:68
msgid "Public IP" msgid "Public IP"
msgstr "公网IP" msgstr "公网IP"
#: assets/models/asset.py:88 assets/templates/assets/asset_detail.html:124 #: assets/models/asset.py:111 assets/templates/assets/asset_detail.html:125
msgid "Asset number" msgid "Asset number"
msgstr "资产编号" msgstr "资产编号"
#: assets/models/asset.py:91 assets/templates/assets/asset_detail.html:88 #: assets/models/asset.py:114 assets/templates/assets/asset_detail.html:89
msgid "Vendor" msgid "Vendor"
msgstr "制造商" msgstr "制造商"
#: assets/models/asset.py:92 assets/templates/assets/asset_detail.html:92 #: assets/models/asset.py:115 assets/templates/assets/asset_detail.html:93
msgid "Model" msgid "Model"
msgstr "型号" msgstr "型号"
#: assets/models/asset.py:93 assets/templates/assets/asset_detail.html:120 #: assets/models/asset.py:116 assets/templates/assets/asset_detail.html:121
msgid "Serial number" msgid "Serial number"
msgstr "序列号" msgstr "序列号"
#: assets/models/asset.py:95 #: assets/models/asset.py:118
msgid "CPU model" msgid "CPU model"
msgstr "CPU型号" msgstr "CPU型号"
#: assets/models/asset.py:96 #: assets/models/asset.py:119
#: xpack/plugins/license/templates/license/license_detail.html:80 #: xpack/plugins/license/templates/license/license_detail.html:80
msgid "CPU count" msgid "CPU count"
msgstr "CPU数量" msgstr "CPU数量"
#: assets/models/asset.py:97 #: assets/models/asset.py:120
msgid "CPU cores" msgid "CPU cores"
msgstr "CPU核数" msgstr "CPU核数"
#: assets/models/asset.py:98 #: assets/models/asset.py:121
msgid "CPU vcpus" msgid "CPU vcpus"
msgstr "CPU总数" msgstr "CPU总数"
#: assets/models/asset.py:99 assets/templates/assets/asset_detail.html:100 #: assets/models/asset.py:122 assets/templates/assets/asset_detail.html:101
msgid "Memory" msgid "Memory"
msgstr "内存" msgstr "内存"
#: assets/models/asset.py:100 #: assets/models/asset.py:123
msgid "Disk total" msgid "Disk total"
msgstr "硬盘大小" msgstr "硬盘大小"
#: assets/models/asset.py:101 #: assets/models/asset.py:124
msgid "Disk info" msgid "Disk info"
msgstr "硬盘信息" msgstr "硬盘信息"
#: assets/models/asset.py:103 assets/templates/assets/asset_detail.html:112 #: assets/models/asset.py:126 assets/templates/assets/asset_detail.html:113
#: assets/templates/assets/user_asset_list.html:171 #: assets/templates/assets/user_asset_list.html:171
msgid "OS" msgid "OS"
msgstr "操作系统" msgstr "操作系统"
#: assets/models/asset.py:104 #: assets/models/asset.py:127
msgid "OS version" msgid "OS version"
msgstr "系统版本" msgstr "系统版本"
#: assets/models/asset.py:105 #: assets/models/asset.py:128
msgid "OS arch" msgid "OS arch"
msgstr "系统架构" msgstr "系统架构"
#: assets/models/asset.py:106 #: assets/models/asset.py:129
msgid "Hostname raw" msgid "Hostname raw"
msgstr "主机名原始" msgstr "主机名原始"
#: assets/models/asset.py:108 assets/templates/assets/asset_create.html:34 #: assets/models/asset.py:131 assets/templates/assets/asset_create.html:46
#: assets/templates/assets/asset_detail.html:231 #: assets/templates/assets/asset_detail.html:232 templates/_nav.html:26
#: assets/templates/assets/asset_update.html:39 templates/_nav.html:26
msgid "Labels" msgid "Labels"
msgstr "标签管理" msgstr "标签管理"
#: assets/models/asset.py:117 assets/models/base.py:38 #: assets/models/asset.py:140 assets/models/base.py:38
#: assets/serializers/admin_user.py:22 assets/serializers/system_user.py:19 #: assets/serializers/admin_user.py:22 assets/serializers/system_user.py:19
#: assets/templates/assets/admin_user_list.html:51 #: assets/templates/assets/admin_user_list.html:51
#: assets/templates/assets/system_user_list.html:57 #: assets/templates/assets/system_user_list.html:57
msgid "Unreachable" msgid "Unreachable"
msgstr "不可达" msgstr "不可达"
#: assets/models/asset.py:118 assets/models/base.py:39 #: assets/models/asset.py:141 assets/models/base.py:39
#: assets/serializers/admin_user.py:24 assets/serializers/system_user.py:27 #: assets/serializers/admin_user.py:24 assets/serializers/system_user.py:27
#: assets/templates/assets/admin_user_assets.html:51 #: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/admin_user_list.html:50 #: assets/templates/assets/admin_user_list.html:50
...@@ -956,7 +955,7 @@ msgstr "不可达" ...@@ -956,7 +955,7 @@ msgstr "不可达"
msgid "Reachable" msgid "Reachable"
msgstr "可连接" msgstr "可连接"
#: assets/models/asset.py:119 assets/models/base.py:40 #: assets/models/asset.py:142 assets/models/base.py:40
#: authentication/utils.py:9 xpack/plugins/license/models.py:78 #: authentication/utils.py:9 xpack/plugins/license/models.py:78
msgid "Unknown" msgid "Unknown"
msgstr "未知" msgstr "未知"
...@@ -975,13 +974,13 @@ msgstr "版本" ...@@ -975,13 +974,13 @@ msgstr "版本"
msgid "AuthBook" msgid "AuthBook"
msgstr "" msgstr ""
#: assets/models/base.py:29 xpack/plugins/change_auth_plan/models.py:94 #: assets/models/base.py:29 xpack/plugins/change_auth_plan/models.py:97
#: xpack/plugins/change_auth_plan/models.py:267 #: xpack/plugins/change_auth_plan/models.py:271
msgid "SSH private key" msgid "SSH private key"
msgstr "ssh密钥" msgstr "ssh密钥"
#: assets/models/base.py:30 xpack/plugins/change_auth_plan/models.py:97 #: assets/models/base.py:30 xpack/plugins/change_auth_plan/models.py:100
#: xpack/plugins/change_auth_plan/models.py:263 #: xpack/plugins/change_auth_plan/models.py:267
msgid "SSH public key" msgid "SSH public key"
msgstr "ssh公钥" msgstr "ssh公钥"
...@@ -1204,18 +1203,22 @@ msgstr "%(value)s is not an even number" ...@@ -1204,18 +1203,22 @@ msgstr "%(value)s is not an even number"
msgid "Date updated" msgid "Date updated"
msgstr "更新日期" msgstr "更新日期"
#: assets/serializers/asset.py:43 #: assets/serializers/asset.py:52
msgid "Hardware info" msgid "Hardware info"
msgstr "硬件信息" msgstr "硬件信息"
#: assets/serializers/asset.py:44 #: assets/serializers/asset.py:53
msgid "Connectivity" msgid "Connectivity"
msgstr "连接" msgstr "连接"
#: assets/serializers/asset.py:45 #: assets/serializers/asset.py:54
msgid "Org name" msgid "Org name"
msgstr "组织名" msgstr "组织名"
#: assets/serializers/asset.py:70
msgid "Protocol duplicate: {}"
msgstr "协议重复: {}"
#: assets/serializers/asset_user.py:23 users/forms.py:247 #: assets/serializers/asset_user.py:23 users/forms.py:247
#: users/models/user.py:91 users/templates/users/first_login.html:42 #: users/models/user.py:91 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:46 #: users/templates/users/user_password_update.html:46
...@@ -1237,72 +1240,86 @@ msgstr "可连接资产" ...@@ -1237,72 +1240,86 @@ msgstr "可连接资产"
msgid "Login mode display" msgid "Login mode display"
msgstr "登录模式显示" msgstr "登录模式显示"
#: assets/tasks.py:31 #: assets/tasks.py:32
msgid "Asset has been disabled, skipped: {}" msgid "Asset has been disabled, skipped: {}"
msgstr "资产或许不支持ansible, 跳过: {}" msgstr "资产或许不支持ansible, 跳过: {}"
#: assets/tasks.py:35 #: assets/tasks.py:36
msgid "Asset may not be support ansible, skipped: {}" msgid "Asset may not be support ansible, skipped: {}"
msgstr "资产或许不支持ansible, 跳过: {}" msgstr "资产或许不支持ansible, 跳过: {}"
#: assets/tasks.py:48 #: assets/tasks.py:49
msgid "No assets matched, stop task" msgid "No assets matched, stop task"
msgstr "没有匹配到资产,结束任务" msgstr "没有匹配到资产,结束任务"
#: assets/tasks.py:73 #: assets/tasks.py:59
msgid "No assets matched related system user protocol, stop task"
msgstr "没有匹配到与系统用户协议相关的资产,结束任务"
#: assets/tasks.py:85
msgid "Get asset info failed: {}" msgid "Get asset info failed: {}"
msgstr "获取资产信息失败:{}" msgstr "获取资产信息失败:{}"
#: assets/tasks.py:123 #: assets/tasks.py:135
msgid "Update some assets hardware info" msgid "Update some assets hardware info"
msgstr "更新资产硬件信息" msgstr "更新资产硬件信息"
#: assets/tasks.py:140 #: assets/tasks.py:152
msgid "Update asset hardware info: {}" msgid "Update asset hardware info: {}"
msgstr "更新资产硬件信息: {}" msgstr "更新资产硬件信息: {}"
#: assets/tasks.py:165 #: assets/tasks.py:177
msgid "Test assets connectivity" msgid "Test assets connectivity"
msgstr "测试资产可连接性" msgstr "测试资产可连接性"
#: assets/tasks.py:189 #: assets/tasks.py:229
msgid "Test assets connectivity: {}" msgid "Test assets connectivity: {}"
msgstr "测试资产可连接性: {}" msgstr "测试资产可连接性: {}"
#: assets/tasks.py:231 #: assets/tasks.py:271
msgid "Test admin user connectivity period: {}" msgid "Test admin user connectivity period: {}"
msgstr "定期测试管理账号可连接性: {}" msgstr "定期测试管理账号可连接性: {}"
#: assets/tasks.py:238 #: assets/tasks.py:278
msgid "Test admin user connectivity: {}" msgid "Test admin user connectivity: {}"
msgstr "测试管理行号可连接性: {}" msgstr "测试管理行号可连接性: {}"
#: assets/tasks.py:277 #: assets/tasks.py:348
msgid "Test system user connectivity: {}" msgid "Test system user connectivity: {}"
msgstr "测试系统用户可连接性: {}" msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:284 #: assets/tasks.py:355
msgid "Test system user connectivity: {} => {}" msgid "Test system user connectivity: {} => {}"
msgstr "测试系统用户可连接性: {} => {}" msgstr "测试系统用户可连接性: {} => {}"
#: assets/tasks.py:297 #: assets/tasks.py:368
msgid "Test system user connectivity period: {}" msgid "Test system user connectivity period: {}"
msgstr "定期测试系统用户可连接性: {}" msgstr "定期测试系统用户可连接性: {}"
#: assets/tasks.py:374 #: assets/tasks.py:469 assets/tasks.py:555
#: xpack/plugins/change_auth_plan/models.py:522
msgid "The asset {} system platform {} does not support run Ansible tasks"
msgstr "资产 {} 系统平台 {} 不支持运行 Ansible 任务"
#: assets/tasks.py:481
msgid "" msgid ""
"Push system user task skip, auto push not enable or protocol is not ssh: {}" "Push system user task skip, auto push not enable or protocol is not ssh or "
msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh: {}" "rdp: {}"
msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}"
#: assets/tasks.py:488
msgid "For security, do not push user {}"
msgstr "为了安全,禁止推送用户 {}"
#: assets/tasks.py:396 assets/tasks.py:410 #: assets/tasks.py:516 assets/tasks.py:530
msgid "Push system users to assets: {}" msgid "Push system users to assets: {}"
msgstr "推送系统用户到入资产: {}" msgstr "推送系统用户到入资产: {}"
#: assets/tasks.py:402 #: assets/tasks.py:522
msgid "Push system users to asset: {} => {}" msgid "Push system users to asset: {} => {}"
msgstr "推送系统用户到入资产: {} => {}" msgstr "推送系统用户到入资产: {} => {}"
#: assets/tasks.py:459 #: assets/tasks.py:600
msgid "Test asset user connectivity: {}" msgid "Test asset user connectivity: {}"
msgstr "测试资产用户可连接性: {}" msgstr "测试资产用户可连接性: {}"
...@@ -1347,7 +1364,7 @@ msgstr "启用MFA" ...@@ -1347,7 +1364,7 @@ msgstr "启用MFA"
msgid "Import assets" msgid "Import assets"
msgstr "导入资产" msgstr "导入资产"
#: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:54 #: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:53
#: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:110 #: templates/_nav.html:22 xpack/plugins/change_auth_plan/views.py:110
msgid "Asset list" msgid "Asset list"
msgstr "资产列表" msgstr "资产列表"
...@@ -1382,7 +1399,7 @@ msgstr "需要二次认证来查看账号信息" ...@@ -1382,7 +1399,7 @@ msgstr "需要二次认证来查看账号信息"
#: assets/templates/assets/_asset_user_view_auth_modal.html:20 #: assets/templates/assets/_asset_user_view_auth_modal.html:20
#: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:211 #: assets/templates/assets/asset_detail.html:212
#: assets/templates/assets/asset_list.html:682 #: assets/templates/assets/asset_list.html:682
#: assets/templates/assets/cmd_filter_detail.html:106 #: assets/templates/assets/cmd_filter_detail.html:106
#: assets/templates/assets/system_user_asset.html:112 #: assets/templates/assets/system_user_asset.html:112
...@@ -1434,7 +1451,6 @@ msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口" ...@@ -1434,7 +1451,6 @@ msgstr "如果使用了nat端口映射,请设置为ssh真实监听的端口"
#: assets/templates/assets/_system_user.html:37 #: assets/templates/assets/_system_user.html:37
#: assets/templates/assets/asset_create.html:16 #: assets/templates/assets/asset_create.html:16
#: assets/templates/assets/asset_update.html:21
#: assets/templates/assets/gateway_create_update.html:37 #: assets/templates/assets/gateway_create_update.html:37
#: perms/templates/perms/asset_permission_create_update.html:38 #: perms/templates/perms/asset_permission_create_update.html:38
#: perms/templates/perms/remote_app_permission_create_update.html:39 #: perms/templates/perms/remote_app_permission_create_update.html:39
...@@ -1443,8 +1459,7 @@ msgid "Basic" ...@@ -1443,8 +1459,7 @@ msgid "Basic"
msgstr "基本" msgstr "基本"
#: assets/templates/assets/_system_user.html:44 #: assets/templates/assets/_system_user.html:44
#: assets/templates/assets/asset_create.html:26 #: assets/templates/assets/asset_create.html:38
#: assets/templates/assets/asset_update.html:31
#: assets/templates/assets/gateway_create_update.html:45 #: assets/templates/assets/gateway_create_update.html:45
#: users/templates/users/_user.html:21 #: users/templates/users/_user.html:21
msgid "Auth" msgid "Auth"
...@@ -1455,8 +1470,7 @@ msgid "Auto generate key" ...@@ -1455,8 +1470,7 @@ msgid "Auto generate key"
msgstr "自动生成密钥" msgstr "自动生成密钥"
#: assets/templates/assets/_system_user.html:69 #: assets/templates/assets/_system_user.html:69
#: assets/templates/assets/asset_create.html:60 #: assets/templates/assets/asset_create.html:74
#: assets/templates/assets/asset_update.html:64
#: 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:52
...@@ -1476,7 +1490,7 @@ msgstr "更新系统用户" ...@@ -1476,7 +1490,7 @@ msgstr "更新系统用户"
#: assets/templates/assets/_user_asset_detail_modal.html:11 #: assets/templates/assets/_user_asset_detail_modal.html:11
#: assets/templates/assets/asset_asset_user_list.html:13 #: assets/templates/assets/asset_asset_user_list.html:13
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:190 #: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:220
msgid "Asset detail" msgid "Asset detail"
msgstr "资产详情" msgstr "资产详情"
...@@ -1501,7 +1515,7 @@ msgstr "快速更新" ...@@ -1501,7 +1515,7 @@ msgstr "快速更新"
#: assets/templates/assets/admin_user_assets.html:70 #: assets/templates/assets/admin_user_assets.html:70
#: assets/templates/assets/asset_asset_user_list.html:67 #: assets/templates/assets/asset_asset_user_list.html:67
#: assets/templates/assets/asset_detail.html:179 #: assets/templates/assets/asset_detail.html:180
msgid "Test connective" msgid "Test connective"
msgstr "测试可连接性" msgstr "测试可连接性"
...@@ -1509,7 +1523,7 @@ msgstr "测试可连接性" ...@@ -1509,7 +1523,7 @@ msgstr "测试可连接性"
#: assets/templates/assets/admin_user_assets.html:118 #: assets/templates/assets/admin_user_assets.html:118
#: assets/templates/assets/asset_asset_user_list.html:70 #: assets/templates/assets/asset_asset_user_list.html:70
#: assets/templates/assets/asset_asset_user_list.html:119 #: assets/templates/assets/asset_asset_user_list.html:119
#: assets/templates/assets/asset_detail.html:182 #: assets/templates/assets/asset_detail.html:183
#: assets/templates/assets/system_user_asset.html:75 #: assets/templates/assets/system_user_asset.html:75
#: assets/templates/assets/system_user_asset.html:166 #: assets/templates/assets/system_user_asset.html:166
#: assets/templates/assets/system_user_detail.html:151 #: assets/templates/assets/system_user_detail.html:151
...@@ -1530,7 +1544,7 @@ msgstr "查看认证" ...@@ -1530,7 +1544,7 @@ msgstr "查看认证"
#: assets/templates/assets/admin_user_assets.html:196 #: assets/templates/assets/admin_user_assets.html:196
#: assets/templates/assets/asset_asset_user_list.html:162 #: assets/templates/assets/asset_asset_user_list.html:162
#: assets/templates/assets/asset_detail.html:311 #: assets/templates/assets/asset_detail.html:312
#: assets/templates/assets/system_user_asset.html:353 #: assets/templates/assets/system_user_asset.html:353
#: users/templates/users/user_detail.html:307 #: users/templates/users/user_detail.html:307
#: users/templates/users/user_detail.html:334 #: users/templates/users/user_detail.html:334
...@@ -1617,7 +1631,7 @@ msgid "Please select file" ...@@ -1617,7 +1631,7 @@ msgid "Please select file"
msgstr "选择文件" msgstr "选择文件"
#: assets/templates/assets/asset_asset_user_list.html:16 #: assets/templates/assets/asset_asset_user_list.html:16
#: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:70 #: assets/templates/assets/asset_detail.html:23 assets/views/asset.py:69
msgid "Asset user list" msgid "Asset user list"
msgstr "资产用户列表" msgstr "资产用户列表"
...@@ -1630,7 +1644,7 @@ msgid "Password version" ...@@ -1630,7 +1644,7 @@ msgid "Password version"
msgstr "密码版本" msgstr "密码版本"
#: assets/templates/assets/asset_asset_user_list.html:60 #: assets/templates/assets/asset_asset_user_list.html:60
#: assets/templates/assets/asset_detail.html:148 #: assets/templates/assets/asset_detail.html:149
#: terminal/templates/terminal/session_detail.html:81 #: terminal/templates/terminal/session_detail.html:81
#: users/templates/users/user_detail.html:138 #: users/templates/users/user_detail.html:138
#: users/templates/users/user_profile.html:146 #: users/templates/users/user_profile.html:146
...@@ -1649,21 +1663,25 @@ msgstr "选择需要修改属性" ...@@ -1649,21 +1663,25 @@ msgstr "选择需要修改属性"
msgid "Select all" msgid "Select all"
msgstr "全选" msgstr "全选"
#: assets/templates/assets/asset_detail.html:96 #: assets/templates/assets/asset_create.html:24
msgid "Protocols"
msgstr "协议"
#: assets/templates/assets/asset_detail.html:97
msgid "CPU" msgid "CPU"
msgstr "CPU" msgstr "CPU"
#: assets/templates/assets/asset_detail.html:104 #: assets/templates/assets/asset_detail.html:105
msgid "Disk" msgid "Disk"
msgstr "硬盘" msgstr "硬盘"
#: assets/templates/assets/asset_detail.html:132 #: assets/templates/assets/asset_detail.html:133
#: users/templates/users/user_detail.html:115 #: users/templates/users/user_detail.html:115
#: users/templates/users/user_profile.html:104 #: users/templates/users/user_profile.html:104
msgid "Date joined" msgid "Date joined"
msgstr "创建日期" msgstr "创建日期"
#: assets/templates/assets/asset_detail.html:154 #: assets/templates/assets/asset_detail.html:155
#: assets/templates/assets/user_asset_list.html:46 #: assets/templates/assets/user_asset_list.html:46
#: perms/models/asset_permission.py:60 perms/models/base.py:38 #: perms/models/asset_permission.py:60 perms/models/base.py:38
#: perms/templates/perms/asset_permission_create_update.html:55 #: perms/templates/perms/asset_permission_create_update.html:55
...@@ -1679,11 +1697,11 @@ msgstr "创建日期" ...@@ -1679,11 +1697,11 @@ msgstr "创建日期"
msgid "Active" msgid "Active"
msgstr "激活中" msgstr "激活中"
#: assets/templates/assets/asset_detail.html:171 #: assets/templates/assets/asset_detail.html:172
msgid "Refresh hardware" msgid "Refresh hardware"
msgstr "更新硬件信息" msgstr "更新硬件信息"
#: assets/templates/assets/asset_detail.html:174 #: assets/templates/assets/asset_detail.html:175
msgid "Refresh" msgid "Refresh"
msgstr "刷新" msgstr "刷新"
...@@ -1696,7 +1714,7 @@ msgstr "" ...@@ -1696,7 +1714,7 @@ msgstr ""
"左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的," "左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,"
"右侧是属于该节点下的资产" "右侧是属于该节点下的资产"
#: assets/templates/assets/asset_list.html:69 assets/views/asset.py:108 #: assets/templates/assets/asset_list.html:69 assets/views/asset.py:126
msgid "Create asset" msgid "Create asset"
msgstr "创建资产" msgstr "创建资产"
...@@ -1830,7 +1848,7 @@ msgstr "删除" ...@@ -1830,7 +1848,7 @@ msgstr "删除"
msgid "Asset Deleting failed." msgid "Asset Deleting failed."
msgstr "删除失败" msgstr "删除失败"
#: assets/templates/assets/asset_update.html:60 #: assets/templates/assets/asset_update.html:7
msgid "Configuration" msgid "Configuration"
msgstr "配置" msgstr "配置"
...@@ -2001,13 +2019,11 @@ msgstr "" ...@@ -2001,13 +2019,11 @@ msgstr ""
#: assets/templates/assets/system_user_list.html:12 #: assets/templates/assets/system_user_list.html:12
msgid "" msgid ""
"When system users are created, if you choose auto push Jumpserver to use " "When system users are created, if you choose auto push Jumpserver to use "
"ansible push system users into the asset, if the asset (Switch, Windows) " "ansible push system users into the asset, if the asset (Switch) does not "
"does not support ansible, please manually fill in the account password. " "support ansible, please manually fill in the account password."
"Automatic push for Windows is not currently supported."
msgstr "" msgstr ""
"系统用户创建时,如果选择了自动推送 Jumpserver会使用ansible自动推送系统用户到" "系统用户创建时,如果选择了自动推送 Jumpserver会使用ansible自动推送系统用户到"
"资产中,如果资产(交换机、windows)不支持ansible, 请手动填写账号密码。目前还不" "资产中,如果资产(交换机)不支持ansible, 请手动填写账号密码。"
"支持Windows的自动推送"
#: assets/templates/assets/system_user_list.html:43 #: assets/templates/assets/system_user_list.html:43
#: assets/views/system_user.py:45 #: assets/views/system_user.py:45
...@@ -2039,23 +2055,23 @@ msgstr "管理用户列表" ...@@ -2039,23 +2055,23 @@ msgstr "管理用户列表"
msgid "Admin user detail" msgid "Admin user detail"
msgstr "管理用户详情" msgstr "管理用户详情"
#: assets/views/asset.py:81 templates/_nav_user.html:4 #: assets/views/asset.py:80 templates/_nav_user.html:4
msgid "My assets" msgid "My assets"
msgstr "我的资产" msgstr "我的资产"
#: assets/views/asset.py:122 #: assets/views/asset.py:141
msgid "Bulk update asset success" msgid "Bulk update asset success"
msgstr "批量更新资产成功" msgstr "批量更新资产成功"
#: assets/views/asset.py:149 #: assets/views/asset.py:168
msgid "Bulk update asset" msgid "Bulk update asset"
msgstr "批量更新资产" msgstr "批量更新资产"
#: assets/views/asset.py:166 #: assets/views/asset.py:195
msgid "Update asset" msgid "Update asset"
msgstr "更新资产" msgstr "更新资产"
#: assets/views/asset.py:307 #: assets/views/asset.py:337
msgid "already exists" msgid "already exists"
msgstr "已经存在" msgstr "已经存在"
...@@ -2223,7 +2239,7 @@ msgid "User agent" ...@@ -2223,7 +2239,7 @@ msgid "User agent"
msgstr "Agent" msgstr "Agent"
#: audits/models.py:100 audits/templates/audits/login_log_list.html:57 #: audits/models.py:100 audits/templates/audits/login_log_list.html:57
#: xpack/plugins/change_auth_plan/models.py:413 #: xpack/plugins/change_auth_plan/models.py:417
#: 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:172 #: xpack/plugins/cloud/models.py:172
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_history.html:69
...@@ -2249,8 +2265,8 @@ msgstr "登录日期" ...@@ -2249,8 +2265,8 @@ 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:78 #: terminal/models.py:165 terminal/templates/terminal/session_list.html:78
#: xpack/plugins/change_auth_plan/models.py:246 #: xpack/plugins/change_auth_plan/models.py:250
#: xpack/plugins/change_auth_plan/models.py:416 #: xpack/plugins/change_auth_plan/models.py:420
#: 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
msgid "Date start" msgid "Date start"
...@@ -2603,18 +2619,35 @@ msgstr "" ...@@ -2603,18 +2619,35 @@ msgstr ""
msgid "Encrypt field using Secret Key" msgid "Encrypt field using Secret Key"
msgstr "" msgstr ""
#: common/mixins.py:35 #: common/mixins.py:36
msgid "is discard" msgid "is discard"
msgstr "" msgstr ""
#: common/mixins.py:36 #: common/mixins.py:37
msgid "discard time" msgid "discard time"
msgstr "" msgstr ""
#: common/validators.py:7 #: common/mixins.py:210
#, python-format
msgid "%(name)s was %(action)s successfully"
msgstr "%(name)s %(action)s成功"
#: common/mixins.py:211
msgid "create"
msgstr "创建"
#: common/mixins.py:211
msgid "update"
msgstr "更新"
#: common/validators.py:11
msgid "Special char not allowed" msgid "Special char not allowed"
msgstr "不能包含特殊字符" msgstr "不能包含特殊字符"
#: common/validators.py:23
msgid "This field must be unique."
msgstr "字段必须唯一"
#: jumpserver/views.py:185 #: jumpserver/views.py:185
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, "
...@@ -2690,48 +2723,48 @@ msgstr "Become" ...@@ -2690,48 +2723,48 @@ msgstr "Become"
msgid "Create by" msgid "Create by"
msgstr "创建者" msgstr "创建者"
#: ops/models/adhoc.py:223 #: ops/models/adhoc.py:224
msgid "{} Start task: {}" msgid "{} Start task: {}"
msgstr "{} 任务开始: {}" msgstr "{} 任务开始: {}"
#: ops/models/adhoc.py:226 #: ops/models/adhoc.py:227
msgid "{} Task finish" msgid "{} Task finish"
msgstr "{} 任务结束" msgstr "{} 任务结束"
#: ops/models/adhoc.py:324 #: ops/models/adhoc.py:325
msgid "Start time" msgid "Start time"
msgstr "开始时间" msgstr "开始时间"
#: ops/models/adhoc.py:325 #: ops/models/adhoc.py:326
msgid "End time" msgid "End time"
msgstr "完成时间" msgstr "完成时间"
#: ops/models/adhoc.py:326 ops/templates/ops/adhoc_history.html:57 #: ops/models/adhoc.py:327 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:249 #: xpack/plugins/change_auth_plan/models.py:253
#: xpack/plugins/change_auth_plan/models.py:419 #: xpack/plugins/change_auth_plan/models.py:423
#: 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
msgid "Time" msgid "Time"
msgstr "时间" msgstr "时间"
#: ops/models/adhoc.py:327 ops/templates/ops/adhoc_detail.html:106 #: ops/models/adhoc.py:328 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:328 ops/templates/ops/adhoc_history.html:56 #: ops/models/adhoc.py:329 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:329 #: ops/models/adhoc.py:330
msgid "Adhoc raw result" msgid "Adhoc raw result"
msgstr "结果" msgstr "结果"
#: ops/models/adhoc.py:330 #: ops/models/adhoc.py:331
msgid "Adhoc result summary" msgid "Adhoc result summary"
msgstr "汇总" msgstr "汇总"
...@@ -2859,33 +2892,33 @@ msgstr "任务列表" ...@@ -2859,33 +2892,33 @@ msgstr "任务列表"
msgid "Go" msgid "Go"
msgstr "" msgstr ""
#: ops/templates/ops/command_execution_create.html:152 #: ops/templates/ops/command_execution_create.html:155
msgid "Selected assets" msgid "Selected assets"
msgstr "已选择资产" msgstr "已选择资产"
#: ops/templates/ops/command_execution_create.html:155 #: ops/templates/ops/command_execution_create.html:158
msgid "In total" msgid "In total"
msgstr "总共" msgstr "总共"
#: ops/templates/ops/command_execution_create.html:190 #: ops/templates/ops/command_execution_create.html:193
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:208 #: ops/templates/ops/command_execution_create.html:211
msgid "Unselected assets" msgid "Unselected assets"
msgstr "没有选中资产" msgstr "没有选中资产"
#: ops/templates/ops/command_execution_create.html:212 #: ops/templates/ops/command_execution_create.html:215
msgid "No input command" msgid "No input command"
msgstr "没有输入命令" msgstr "没有输入命令"
#: ops/templates/ops/command_execution_create.html:216 #: ops/templates/ops/command_execution_create.html:219
msgid "No system user was selected" msgid "No system user was selected"
msgstr "没有选择系统用户" msgstr "没有选择系统用户"
#: ops/templates/ops/command_execution_create.html:261 #: ops/templates/ops/command_execution_create.html:264
msgid "Pending" msgid "Pending"
msgstr "等待" msgstr "等待"
...@@ -2966,7 +2999,7 @@ msgstr "命令执行列表" ...@@ -2966,7 +2999,7 @@ msgstr "命令执行列表"
msgid "Command execution" msgid "Command execution"
msgstr "命令执行" msgstr "命令执行"
#: orgs/mixins.py:81 orgs/models.py:24 #: orgs/mixins.py:83 orgs/models.py:24
msgid "Organization" msgid "Organization"
msgstr "组织管理" msgstr "组织管理"
...@@ -3167,12 +3200,12 @@ msgstr "添加用户组" ...@@ -3167,12 +3200,12 @@ 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:80 perms/views/asset_permission.py:95 #: perms/views/asset_permission.py:80 perms/views/asset_permission.py:95
#: perms/views/asset_permission.py:130 perms/views/asset_permission.py:162 #: perms/views/asset_permission.py:130 perms/views/asset_permission.py:162
#: perms/views/remote_app_permission.py:33 #: perms/views/remote_app_permission.py:32
#: perms/views/remote_app_permission.py:48 #: perms/views/remote_app_permission.py:47
#: perms/views/remote_app_permission.py:63 #: perms/views/remote_app_permission.py:62
#: perms/views/remote_app_permission.py:76 #: perms/views/remote_app_permission.py:75
#: perms/views/remote_app_permission.py:102 #: perms/views/remote_app_permission.py:101
#: perms/views/remote_app_permission.py:138 templates/_nav.html:41 #: perms/views/remote_app_permission.py:137 templates/_nav.html:41
#: xpack/plugins/orgs/templates/orgs/org_list.html:21 #: xpack/plugins/orgs/templates/orgs/org_list.html:21
msgid "Perms" msgid "Perms"
msgstr "权限管理" msgstr "权限管理"
...@@ -3201,27 +3234,27 @@ msgstr "资产授权用户列表" ...@@ -3201,27 +3234,27 @@ msgstr "资产授权用户列表"
msgid "Asset permission asset list" msgid "Asset permission asset list"
msgstr "资产授权资产列表" msgstr "资产授权资产列表"
#: perms/views/remote_app_permission.py:34 #: perms/views/remote_app_permission.py:33
msgid "RemoteApp permission list" msgid "RemoteApp permission list"
msgstr "远程应用授权列表" msgstr "远程应用授权列表"
#: perms/views/remote_app_permission.py:49 #: perms/views/remote_app_permission.py:48
msgid "Create RemoteApp permission" msgid "Create RemoteApp permission"
msgstr "创建远程应用授权规则" msgstr "创建远程应用授权规则"
#: perms/views/remote_app_permission.py:64 #: perms/views/remote_app_permission.py:63
msgid "Update RemoteApp permission" msgid "Update RemoteApp permission"
msgstr "更新远程应用授权规则" msgstr "更新远程应用授权规则"
#: perms/views/remote_app_permission.py:77 #: perms/views/remote_app_permission.py:76
msgid "RemoteApp permission detail" msgid "RemoteApp permission detail"
msgstr "远程应用授权详情" msgstr "远程应用授权详情"
#: perms/views/remote_app_permission.py:103 #: perms/views/remote_app_permission.py:102
msgid "RemoteApp permission user list" msgid "RemoteApp permission user list"
msgstr "远程应用授权用户列表" msgstr "远程应用授权用户列表"
#: perms/views/remote_app_permission.py:139 #: perms/views/remote_app_permission.py:138
msgid "RemoteApp permission RemoteApp list" msgid "RemoteApp permission RemoteApp list"
msgstr "远程应用授权远程应用列表" msgstr "远程应用授权远程应用列表"
...@@ -4380,7 +4413,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户" ...@@ -4380,7 +4413,7 @@ msgstr "生成重置密码链接,通过邮件发送给用户"
msgid "Set password" msgid "Set password"
msgstr "设置密码" msgstr "设置密码"
#: users/forms.py:117 xpack/plugins/change_auth_plan/models.py:83 #: users/forms.py:117 xpack/plugins/change_auth_plan/models.py:86
#: 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
...@@ -5284,23 +5317,17 @@ msgstr "定时执行" ...@@ -5284,23 +5317,17 @@ msgstr "定时执行"
#: xpack/plugins/change_auth_plan/forms.py:120 #: xpack/plugins/change_auth_plan/forms.py:120
msgid "" msgid ""
"Tips: Currently only unix-like assets are supported, while Windows assets "
"are not"
msgstr "提示:目前仅支持类Unix资产,暂不支持Windows资产"
#: xpack/plugins/change_auth_plan/forms.py:122
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."
msgstr "" msgstr ""
"提示:用户名为将要修改的资产上的用户的用户名。如果用户存在,则修改密码;如果" "提示:用户名为将要修改的资产上的用户的用户名。如果用户存在,则修改密码;如果"
"用户不存在,则创建用户。" "用户不存在,则创建用户。"
#: xpack/plugins/change_auth_plan/forms.py:126 #: xpack/plugins/change_auth_plan/forms.py:124
msgid "Tips: (Units: hour)" msgid "Tips: (Units: hour)"
msgstr "提示:(单位: 时)" msgstr "提示:(单位: 时)"
#: xpack/plugins/change_auth_plan/forms.py:127 #: xpack/plugins/change_auth_plan/forms.py:125
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/"
...@@ -5312,8 +5339,8 @@ msgstr "" ...@@ -5312,8 +5339,8 @@ 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:111 #: xpack/plugins/change_auth_plan/models.py:114
#: xpack/plugins/change_auth_plan/models.py:253 #: xpack/plugins/change_auth_plan/models.py:257
#: xpack/plugins/change_auth_plan/views.py:31 #: xpack/plugins/change_auth_plan/views.py:31
#: xpack/plugins/change_auth_plan/views.py:47 #: xpack/plugins/change_auth_plan/views.py:47
#: xpack/plugins/change_auth_plan/views.py:68 #: xpack/plugins/change_auth_plan/views.py:68
...@@ -5324,61 +5351,61 @@ msgstr "" ...@@ -5324,61 +5351,61 @@ msgstr ""
msgid "Change auth plan" msgid "Change auth plan"
msgstr "改密计划" msgstr "改密计划"
#: xpack/plugins/change_auth_plan/models.py:52 #: xpack/plugins/change_auth_plan/models.py:55
msgid "Custom password" msgid "Custom password"
msgstr "自定义密码" msgstr "自定义密码"
#: xpack/plugins/change_auth_plan/models.py:53 #: xpack/plugins/change_auth_plan/models.py:56
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:54 #: xpack/plugins/change_auth_plan/models.py:57
msgid "All assets use different random password" msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码" msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:73 #: xpack/plugins/change_auth_plan/models.py:76
#: xpack/plugins/change_auth_plan/models.py:142 #: xpack/plugins/change_auth_plan/models.py:145
#: 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
msgid "Cycle perform" msgid "Cycle perform"
msgstr "周期执行" msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:78 #: xpack/plugins/change_auth_plan/models.py:81
#: xpack/plugins/change_auth_plan/models.py:140 #: xpack/plugins/change_auth_plan/models.py:143
#: 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
msgid "Regularly perform" msgid "Regularly perform"
msgstr "定期执行" msgstr "定期执行"
#: xpack/plugins/change_auth_plan/models.py:87 #: xpack/plugins/change_auth_plan/models.py:90
#: 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:210
msgid "For security, do not change root user's password"
msgstr "为了安全,禁止更改root用户的密码"
#: xpack/plugins/change_auth_plan/models.py:213 #: xpack/plugins/change_auth_plan/models.py:213
msgid "For security, do not change {} user's password"
msgstr "为了安全,禁止更改 {} 用户的密码"
#: xpack/plugins/change_auth_plan/models.py:217
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:257 #: xpack/plugins/change_auth_plan/models.py:261
msgid "Change auth plan snapshot" msgid "Change auth plan snapshot"
msgstr "改密计划快照" msgstr "改密计划快照"
#: xpack/plugins/change_auth_plan/models.py:272 #: xpack/plugins/change_auth_plan/models.py:276
#: xpack/plugins/change_auth_plan/models.py:423 #: xpack/plugins/change_auth_plan/models.py:427
msgid "Change auth plan execution" msgid "Change auth plan execution"
msgstr "改密计划执行" msgstr "改密计划执行"
#: xpack/plugins/change_auth_plan/models.py:432 #: xpack/plugins/change_auth_plan/models.py:436
msgid "Change auth plan execution subtask" msgid "Change auth plan execution subtask"
msgstr "改密计划执行子任务" msgstr "改密计划执行子任务"
#: xpack/plugins/change_auth_plan/models.py:450 #: xpack/plugins/change_auth_plan/models.py:454
msgid "Authentication failed" msgid "Authentication failed"
msgstr "认证失败" msgstr "认证失败"
#: xpack/plugins/change_auth_plan/models.py:452 #: xpack/plugins/change_auth_plan/models.py:456
msgid "Connection timeout" msgid "Connection timeout"
msgstr "连接超时" msgstr "连接超时"
......
...@@ -21,7 +21,7 @@ class JMSBaseInventory(BaseInventory): ...@@ -21,7 +21,7 @@ class JMSBaseInventory(BaseInventory):
'id': asset.id, 'id': asset.id,
'hostname': asset.hostname, 'hostname': asset.hostname,
'ip': asset.ip, 'ip': asset.ip,
'port': asset.port, 'port': asset.ssh_port,
'vars': dict(), 'vars': dict(),
'groups': [], 'groups': [],
} }
...@@ -29,8 +29,15 @@ class JMSBaseInventory(BaseInventory): ...@@ -29,8 +29,15 @@ class JMSBaseInventory(BaseInventory):
info["vars"].update(self.make_proxy_command(asset)) info["vars"].update(self.make_proxy_command(asset))
if run_as_admin: if run_as_admin:
info.update(asset.get_auth_info()) info.update(asset.get_auth_info())
if asset.is_unixlike():
info["become"] = asset.admin_user.become_info
for node in asset.nodes.all(): for node in asset.nodes.all():
info["groups"].append(node.value) info["groups"].append(node.value)
if asset.is_windows():
info["vars"].update({
"ansible_connection": "ssh",
"ansible_shell_type": "cmd",
})
for label in asset.labels.all(): for label in asset.labels.all():
info["vars"].update({ info["vars"].update({
label.name: label.value label.name: label.value
...@@ -73,7 +80,7 @@ class JMSInventory(JMSBaseInventory): ...@@ -73,7 +80,7 @@ class JMSInventory(JMSBaseInventory):
""" """
def __init__(self, assets, run_as_admin=False, run_as=None, become_info=None): def __init__(self, assets, run_as_admin=False, run_as=None, become_info=None):
""" """
:param host_id_list: ["test1", ] :param assets: assets
:param run_as_admin: True 是否使用管理用户去执行, 每台服务器的管理用户可能不同 :param run_as_admin: True 是否使用管理用户去执行, 每台服务器的管理用户可能不同
:param run_as: 用户名(添加了统一的资产用户管理器之后AssetUserManager加上之后修改为username) :param run_as: 用户名(添加了统一的资产用户管理器之后AssetUserManager加上之后修改为username)
:param become_info: 是否become成某个用户去执行 :param become_info: 是否become成某个用户去执行
...@@ -86,17 +93,14 @@ class JMSInventory(JMSBaseInventory): ...@@ -86,17 +93,14 @@ class JMSInventory(JMSBaseInventory):
host_list = [] host_list = []
for asset in assets: for asset in assets:
info = self.convert_to_ansible(asset, run_as_admin=run_as_admin) host = self.convert_to_ansible(asset, run_as_admin=run_as_admin)
host_list.append(info) if run_as:
if run_as:
for host in host_list:
run_user_info = self.get_run_user_info(host) run_user_info = self.get_run_user_info(host)
host.update(run_user_info) host.update(run_user_info)
if become_info and asset.is_unixlike():
if become_info:
for host in host_list:
host.update(become_info) host.update(become_info)
host_list.append(host)
super().__init__(host_list=host_list) super().__init__(host_list=host_list)
def get_run_user_info(self, host): def get_run_user_info(self, host):
...@@ -133,12 +137,10 @@ class JMSCustomInventory(JMSBaseInventory): ...@@ -133,12 +137,10 @@ class JMSCustomInventory(JMSBaseInventory):
host_list = [] host_list = []
for asset in assets: for asset in assets:
info = self.convert_to_ansible(asset) host = self.convert_to_ansible(asset)
host_list.append(info)
for host in host_list:
run_user_info = self.get_run_user_info() run_user_info = self.get_run_user_info()
host.update(run_user_info) host.update(run_user_info)
host_list.append(host)
super().__init__(host_list=host_list) super().__init__(host_list=host_list)
......
...@@ -220,6 +220,7 @@ class AdHoc(models.Model): ...@@ -220,6 +220,7 @@ class AdHoc(models.Model):
time_start = time.time() time_start = time.time()
try: try:
date_start = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') date_start = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
history.date_start = date_start
print(_("{} Start task: {}").format(date_start, 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') date_end = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
......
...@@ -134,8 +134,11 @@ function getSelectedAssetsNode() { ...@@ -134,8 +134,11 @@ function getSelectedAssetsNode() {
var assetsNodeId = []; var assetsNodeId = [];
var assetsNode = []; var assetsNode = [];
nodes.forEach(function (node) { nodes.forEach(function (node) {
if (node.meta.type === 'asset' && !node.isHidden && node.meta.asset.protocol === 'ssh') { if (node.meta.type === 'asset' && !node.isHidden) {
if (assetsNodeId.indexOf(node.id) === -1) { var protocols = $.map(node.meta.asset.protocols, function (v) {
return v.name
});
if (assetsNodeId.indexOf(node.id) === -1 && protocols.indexOf("ssh") > -1) {
assetsNodeId.push(node.id); assetsNodeId.push(node.id);
assetsNode.push(node) assetsNode.push(node)
} }
......
...@@ -18,7 +18,7 @@ def update_or_create_ansible_task( ...@@ -18,7 +18,7 @@ def update_or_create_ansible_task(
run_as_admin=False, run_as=None, become_info=None, run_as_admin=False, run_as=None, become_info=None,
): ):
if not hosts or not tasks or not task_name: if not hosts or not tasks or not task_name:
return return None, None
set_to_root_org() set_to_root_org()
defaults = { defaults = {
'name': task_name, 'name': task_name,
......
...@@ -9,8 +9,10 @@ from django.forms import ModelForm ...@@ -9,8 +9,10 @@ from django.forms import ModelForm
from django.http.response import HttpResponseForbidden from django.http.response import HttpResponseForbidden
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from rest_framework import serializers from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator
from common.utils import get_logger from common.utils import get_logger
from common.validators import ProjectUniqueValidator
from .utils import ( from .utils import (
current_org, set_current_org, set_to_root_org, get_current_org_id current_org, set_current_org, set_to_root_org, get_current_org_id
) )
...@@ -216,3 +218,14 @@ class OrgResourceSerializerMixin(serializers.Serializer): ...@@ -216,3 +218,14 @@ class OrgResourceSerializerMixin(serializers.Serializer):
但是coco需要资产的org_id字段,所以修改为CharField类型 但是coco需要资产的org_id字段,所以修改为CharField类型
""" """
org_id = serializers.CharField(default=get_current_org_id) org_id = serializers.CharField(default=get_current_org_id)
def get_validators(self):
_validators = super().get_validators()
validators = []
for v in _validators:
if isinstance(v, UniqueTogetherValidator) \
and "org_id" in v.fields:
v = ProjectUniqueValidator(v.queryset, v.fields)
validators.append(v)
return validators
...@@ -154,7 +154,7 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIV ...@@ -154,7 +154,7 @@ class UserGrantedAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ListAPIV
util = AssetPermissionUtil(user, cache_policy=self.cache_policy) util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
assets = util.get_assets() assets = util.get_assets()
for k, v in assets.items(): for k, v in assets.items():
system_users_granted = [s for s in v if s.protocol == k.protocol] system_users_granted = [s for s in v if k.has_protocol(s.protocol)]
k.system_users_granted = system_users_granted k.system_users_granted = system_users_granted
queryset.append(k) queryset.append(k)
return queryset return queryset
...@@ -215,8 +215,7 @@ class UserGrantedNodesWithAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin, ...@@ -215,8 +215,7 @@ class UserGrantedNodesWithAssetsApi(UserPermissionCacheMixin, AssetsFilterMixin,
for node, _assets in nodes.items(): for node, _assets in nodes.items():
assets = _assets.keys() assets = _assets.keys()
for k, v in _assets.items(): for k, v in _assets.items():
system_users_granted = [s for s in v if system_users_granted = [s for s in v if k.has_protocol(s.protocol)]
s.protocol == k.protocol]
k.system_users_granted = system_users_granted k.system_users_granted = system_users_granted
node.assets_granted = assets node.assets_granted = assets
queryset.append(node) queryset.append(node)
...@@ -364,7 +363,7 @@ class UserGrantedNodeChildrenApi(UserPermissionCacheMixin, ListAPIView): ...@@ -364,7 +363,7 @@ class UserGrantedNodeChildrenApi(UserPermissionCacheMixin, ListAPIView):
for asset, system_users in nodes_granted[node].items(): for asset, system_users in nodes_granted[node].items():
fake_node = asset.as_node() fake_node = asset.as_node()
fake_node.assets_amount = 0 fake_node.assets_amount = 0
system_users = [s for s in system_users if s.protocol == asset.protocol] system_users = [s for s in system_users if asset.has_protocol(s.protocol)]
fake_node.asset.system_users_granted = system_users fake_node.asset.system_users_granted = system_users
fake_node.key = node.key + ':0' fake_node.key = node.key + ':0'
fake_nodes.append(fake_node) fake_nodes.append(fake_node)
...@@ -389,7 +388,7 @@ class UserGrantedNodeChildrenApi(UserPermissionCacheMixin, ListAPIView): ...@@ -389,7 +388,7 @@ class UserGrantedNodeChildrenApi(UserPermissionCacheMixin, ListAPIView):
fake_node = asset.as_node() fake_node = asset.as_node()
fake_node.assets_amount = 0 fake_node.assets_amount = 0
system_users = [s for s in system_users if system_users = [s for s in system_users if
s.protocol == asset.protocol] asset.has_protocol(s.protocol)]
fake_node.asset.system_users_granted = system_users fake_node.asset.system_users_granted = system_users
fake_node.key = node.key + ':0' fake_node.key = node.key + ':0'
matched_assets.append(fake_node) matched_assets.append(fake_node)
......
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset, SystemUser, Node, RemoteApp from assets.models import Asset, SystemUser, Node
from assets.serializers import ( from assets.serializers import (
AssetGrantedSerializer, NodeSerializer AssetGrantedSerializer, NodeSerializer
) )
from applications.serializers import RemoteAppSerializer from applications.serializers import RemoteAppSerializer
from applications.models import RemoteApp
...@@ -238,7 +238,7 @@ class AssetPermissionUtil: ...@@ -238,7 +238,7 @@ class AssetPermissionUtil:
for perm in permissions: for perm in permissions:
actions = perm.actions.all() actions = perm.actions.all()
for asset in perm.assets.all().valid().prefetch_related('nodes'): for asset in perm.assets.all().valid().prefetch_related('nodes'):
system_users = perm.system_users.filter(protocol=asset.protocol) system_users = perm.system_users.filter(protocol__in=asset.protocols_name)
system_users = self._structured_system_user(system_users, actions) system_users = self._structured_system_user(system_users, actions)
assets[asset].update(system_users) assets[asset].update(system_users)
return assets return assets
...@@ -255,7 +255,7 @@ class AssetPermissionUtil: ...@@ -255,7 +255,7 @@ class AssetPermissionUtil:
_assets = node.get_all_assets().valid().prefetch_related('nodes') _assets = node.get_all_assets().valid().prefetch_related('nodes')
for asset in _assets: for asset in _assets:
for system_user, attr_dict in system_users.items(): for system_user, attr_dict in system_users.items():
if system_user.protocol != asset.protocol: if not asset.has_protocol(system_user.protocol):
continue continue
if system_user in assets[asset]: if system_user in assets[asset]:
actions = assets[asset][system_user]['actions'] actions = assets[asset][system_user]['actions']
...@@ -279,15 +279,12 @@ class AssetPermissionUtil: ...@@ -279,15 +279,12 @@ class AssetPermissionUtil:
resource=resource resource=resource
) )
@property
def node_key(self): def node_key(self):
return self.get_cache_key('NODES_WITH_ASSETS') return self.get_cache_key('NODES_WITH_ASSETS')
@property
def asset_key(self): def asset_key(self):
return self.get_cache_key('ASSETS') return self.get_cache_key('ASSETS')
@property
def system_key(self): def system_key(self):
return self.get_cache_key('SYSTEM_USER') return self.get_cache_key('SYSTEM_USER')
...@@ -457,7 +454,7 @@ def parse_node_to_tree_node(node): ...@@ -457,7 +454,7 @@ def parse_node_to_tree_node(node):
def parse_asset_to_tree_node(node, asset, system_users): def parse_asset_to_tree_node(node, asset, system_users):
system_users_protocol_matched = [s for s in system_users if s.protocol == asset.protocol] system_users_protocol_matched = [s for s in system_users if asset.has_protocol(s.protocol)]
icon_skin = 'file' icon_skin = 'file'
if asset.platform.lower() == 'windows': if asset.platform.lower() == 'windows':
icon_skin = 'windows' icon_skin = 'windows'
...@@ -490,8 +487,8 @@ def parse_asset_to_tree_node(node, asset, system_users): ...@@ -490,8 +487,8 @@ def parse_asset_to_tree_node(node, asset, system_users):
'id': asset.id, 'id': asset.id,
'hostname': asset.hostname, 'hostname': asset.hostname,
'ip': asset.ip, 'ip': asset.ip,
'port': asset.port, 'protocols': [{"name": p.name, "port": p.port}
'protocol': asset.protocol, for p in asset.protocols.all()],
'platform': asset.platform, 'platform': asset.platform,
'domain': None if not asset.domain else asset.domain.id, 'domain': None if not asset.domain else asset.domain.id,
'is_active': asset.is_active, 'is_active': asset.is_active,
......
...@@ -11,9 +11,8 @@ from django.conf import settings ...@@ -11,9 +11,8 @@ from django.conf import settings
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from orgs.utils import current_org from orgs.utils import current_org
from users.models import UserGroup
from assets.models import RemoteApp
from ..hands import RemoteApp, UserGroup
from ..models import RemoteAppPermission from ..models import RemoteAppPermission
from ..forms import RemoteAppPermissionCreateUpdateForm from ..forms import RemoteAppPermissionCreateUpdateForm
......
...@@ -453,4 +453,16 @@ div.dataTables_wrapper div.dataTables_filter { ...@@ -453,4 +453,16 @@ div.dataTables_wrapper div.dataTables_filter {
#tree-refresh .fa-refresh { #tree-refresh .fa-refresh {
font: normal normal normal 14px/1 FontAwesome !important; font: normal normal normal 14px/1 FontAwesome !important;
} }
\ No newline at end of file
.select2-selection__rendered span.select2-selection, .select2-container .select2-selection--single, .select2-selection__arrow {
height: 34px !important;
}
.select2-selection {
border-radius: 0 !important;
}
span.select2-selection__placeholder {
line-height: 34px !important;
}
...@@ -165,11 +165,13 @@ function formSubmit(props) { ...@@ -165,11 +165,13 @@ function formSubmit(props) {
/* /*
{ {
"form": $("form"), "form": $("form"),
"data": {},
"url": "", "url": "",
"method": "POST", "method": "POST",
"redirect_to": "", "redirect_to": "",
"success": function(data, textStatue, jqXHR){}, "success": function(data, textStatue, jqXHR){},
"error": function(jqXHR, textStatus, errorThrown) {} "error": function(jqXHR, textStatus, errorThrown) {},
"message": "",
} }
*/ */
props = props || {}; props = props || {};
...@@ -183,6 +185,10 @@ function formSubmit(props) { ...@@ -183,6 +185,10 @@ function formSubmit(props) {
dataType: props.data_type || "json" dataType: props.data_type || "json"
}).done(function (data, textState, jqXHR) { }).done(function (data, textState, jqXHR) {
if (redirect_to) { if (redirect_to) {
if (props.message) {
var messages="ed65330a45559c87345a0eb6ac7812d18d0d8976$[[\"__json_message\"\0540\05425\054\"asdfasdf \\u521b\\u5efa\\u6210\\u529f\"]]"
setCookie("messages", messages)
}
location.href = redirect_to; location.href = redirect_to;
} else if (typeof props.success === 'function') { } else if (typeof props.success === 'function') {
return props.success(data, textState, jqXHR); return props.success(data, textState, jqXHR);
...@@ -230,7 +236,15 @@ function formSubmit(props) { ...@@ -230,7 +236,15 @@ function formSubmit(props) {
var help_msg = v.join("<br/>") ; var help_msg = v.join("<br/>") ;
helpBlockRef.html(help_msg); helpBlockRef.html(help_msg);
} else { } else {
noneFieldErrorMsg += v + '<br/>'; $.each(v, function (kk, vv) {
if (typeof errors === "object") {
$.each(vv, function (kkk, vvv) {
noneFieldErrorMsg += " " + vvv + '<br/>';
})
} else{
noneFieldErrorMsg += vv + '<br/>';
}
})
} }
}); });
if (noneFieldErrorRef.length === 1 && noneFieldErrorMsg !== '') { if (noneFieldErrorRef.length === 1 && noneFieldErrorMsg !== '') {
......
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