Commit 90b77fdb authored by ibuler's avatar ibuler

Merge branch 'dev' of github.com:jumpserver/jumpserver into dev

parents 068a2803 a609f170
......@@ -14,6 +14,7 @@
# limitations under the License.
from django.db import transaction
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet
......@@ -24,13 +25,14 @@ from common.utils import get_logger
from ..hands import IsOrgAdmin
from ..models import AdminUser, Asset
from .. import serializers
from ..tasks import test_admin_user_connectability_manual
from ..tasks import test_admin_user_connectivity_manual
logger = get_logger(__file__)
__all__ = [
'AdminUserViewSet', 'ReplaceNodesAdminUserApi',
'AdminUserTestConnectiveApi', 'AdminUserAuthApi',
'AdminUserAssetsListView',
]
......@@ -81,12 +83,29 @@ class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
"""
Test asset admin user connectivity
Test asset admin user assets_connectivity
"""
queryset = AdminUser.objects.all()
permission_classes = (IsOrgAdmin,)
def retrieve(self, request, *args, **kwargs):
admin_user = self.get_object()
task = test_admin_user_connectability_manual.delay(admin_user)
task = test_admin_user_connectivity_manual.delay(admin_user)
return Response({"task": task.id})
class AdminUserAssetsListView(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSimpleSerializer
pagination_class = LimitOffsetPagination
filter_fields = ("hostname", "ip")
http_method_names = ['get']
search_fields = filter_fields
def get_object(self):
pk = self.kwargs.get('pk')
return get_object_or_404(AdminUser, pk=pk)
def get_queryset(self):
admin_user = self.get_object()
return admin_user.get_related_assets()
......@@ -17,7 +17,7 @@ from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from ..models import Asset, AdminUser, Node
from .. import serializers
from ..tasks import update_asset_hardware_info_manual, \
test_asset_connectability_manual
test_asset_connectivity_manual
from ..utils import LabelFilter
......@@ -109,7 +109,7 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView):
class AssetAdminUserTestApi(generics.RetrieveAPIView):
"""
Test asset admin user connectivity
Test asset admin user assets_connectivity
"""
queryset = Asset.objects.all()
permission_classes = (IsOrgAdmin,)
......@@ -117,7 +117,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
asset_id = kwargs.get('pk')
asset = get_object_or_404(Asset, pk=asset_id)
task = test_asset_connectability_manual.delay(asset)
task = test_asset_connectivity_manual.delay(asset)
return Response({"task": task.id})
......
......@@ -24,7 +24,7 @@ from common.utils import get_logger, get_object_or_none
from common.tree import TreeNodeSerializer
from ..hands import IsOrgAdmin
from ..models import Node
from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util
from ..tasks import update_assets_hardware_info_util, test_asset_connectivity_util
from .. import serializers
......@@ -273,5 +273,5 @@ class TestNodeConnectiveApi(APIView):
assets = node.assets.all()
# task_name = _("测试节点下资产是否可连接: {}".format(node.name))
task_name = _("Test if the assets under the node are connectable: {}".format(node.name))
task = test_asset_connectability_util.delay(assets, task_name=task_name)
task = test_asset_connectivity_util.delay(assets, task_name=task_name)
return Response({"task": task.id})
......@@ -24,8 +24,8 @@ from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
from ..models import SystemUser, Asset
from .. import serializers
from ..tasks import push_system_user_to_assets_manual, \
test_system_user_connectability_manual, push_system_user_a_asset_manual, \
test_system_user_connectability_a_asset
test_system_user_connectivity_manual, push_system_user_a_asset_manual, \
test_system_user_connectivity_a_asset
logger = get_logger(__file__)
......@@ -33,7 +33,7 @@ __all__ = [
'SystemUserViewSet', 'SystemUserAuthInfoApi',
'SystemUserPushApi', 'SystemUserTestConnectiveApi',
'SystemUserAssetsListView', 'SystemUserPushToAssetApi',
'SystemUserTestAssetConnectabilityApi', 'SystemUserCommandFilterRuleListApi',
'SystemUserTestAssetConnectivityApi', 'SystemUserCommandFilterRuleListApi',
]
......@@ -93,15 +93,16 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
task = test_system_user_connectability_manual.delay(system_user)
task = test_system_user_connectivity_manual.delay(system_user)
return Response({"task": task.id})
class SystemUserAssetsListView(generics.ListAPIView):
permission_classes = (IsOrgAdmin,)
serializer_class = serializers.AssetSerializer
serializer_class = serializers.AssetSimpleSerializer
pagination_class = LimitOffsetPagination
filter_fields = ("hostname", "ip")
http_method_names = ['get']
search_fields = filter_fields
def get_object(self):
......@@ -125,7 +126,7 @@ class SystemUserPushToAssetApi(generics.RetrieveAPIView):
return Response({"task": task.id})
class SystemUserTestAssetConnectabilityApi(generics.RetrieveAPIView):
class SystemUserTestAssetConnectivityApi(generics.RetrieveAPIView):
queryset = SystemUser.objects.all()
permission_classes = (IsOrgAdmin,)
......@@ -133,7 +134,7 @@ class SystemUserTestAssetConnectabilityApi(generics.RetrieveAPIView):
system_user = self.get_object()
asset_id = self.kwargs.get('aid')
asset = get_object_or_404(Asset, id=asset_id)
task = test_system_user_connectability_a_asset.delay(system_user, asset)
task = test_system_user_connectivity_a_asset.delay(system_user, asset)
return Response({"task": task.id})
......
......@@ -99,8 +99,8 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
auto_generate_key = self.cleaned_data.get('auto_generate_key', False)
private_key, public_key = super().gen_keys()
if login_mode == SystemUser.MANUAL_LOGIN or \
protocol in [SystemUser.RDP_PROTOCOL, SystemUser.TELNET_PROTOCOL]:
if login_mode == SystemUser.LOGIN_MANUAL or \
protocol in [SystemUser.PROTOCOL_RDP, SystemUser.PROTOCOL_TELNET]:
system_user.auto_push = 0
auto_generate_key = False
system_user.save()
......@@ -124,7 +124,7 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
validated = super().is_valid()
username = self.cleaned_data.get('username')
login_mode = self.cleaned_data.get('login_mode')
if login_mode == SystemUser.AUTO_LOGIN and not username:
if login_mode == SystemUser.LOGIN_AUTO and not username:
self.add_error(
"username", _('* Automatic login mode,'
' must fill in the username.')
......
......@@ -13,7 +13,6 @@ from django.db.models import Q
from django.utils.translation import ugettext_lazy as _
from django.core.cache import cache
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
from .user import AdminUser, SystemUser
from orgs.mixins import OrgModelMixin, OrgManager
......@@ -75,63 +74,48 @@ class Asset(OrgModelMixin):
protocol = models.CharField(max_length=128, default=SSH_PROTOCOL, choices=PROTOCOL_CHOICES, verbose_name=_('Protocol'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
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)
nodes = models.ManyToManyField('assets.Node', default=default_node,
related_name='assets',
verbose_name=_("Nodes"))
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"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
# Auth
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
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
# Collect
vendor = models.CharField(max_length=64, null=True, blank=True,
verbose_name=_('Vendor'))
model = models.CharField(max_length=54, null=True, blank=True,
verbose_name=_('Model'))
sn = models.CharField(max_length=128, null=True, blank=True,
verbose_name=_('Serial number'))
cpu_model = models.CharField(max_length=64, null=True, blank=True,
verbose_name=_('CPU model'))
vendor = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Vendor'))
model = models.CharField(max_length=54, null=True, blank=True, verbose_name=_('Model'))
sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Serial number'))
cpu_model = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU model'))
cpu_count = models.IntegerField(null=True, verbose_name=_('CPU count'))
cpu_cores = models.IntegerField(null=True, verbose_name=_('CPU cores'))
cpu_vcpus = models.IntegerField(null=True, verbose_name=_('CPU vcpus'))
memory = models.CharField(max_length=64, null=True, blank=True,
verbose_name=_('Memory'))
disk_total = models.CharField(max_length=1024, null=True, blank=True,
verbose_name=_('Disk total'))
disk_info = models.CharField(max_length=1024, null=True, blank=True,
verbose_name=_('Disk info'))
os = models.CharField(max_length=128, null=True, blank=True,
verbose_name=_('OS'))
os_version = models.CharField(max_length=16, null=True, blank=True,
verbose_name=_('OS version'))
os_arch = models.CharField(max_length=16, blank=True, null=True,
verbose_name=_('OS arch'))
hostname_raw = models.CharField(max_length=128, blank=True, null=True,
verbose_name=_('Hostname raw'))
labels = models.ManyToManyField('assets.Label', blank=True,
related_name='assets',
verbose_name=_("Labels"))
created_by = models.CharField(max_length=32, null=True, blank=True,
verbose_name=_('Created by'))
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'))
memory = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Memory'))
disk_total = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk total'))
disk_info = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk info'))
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS'))
os_version = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('OS version'))
os_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('OS arch'))
hostname_raw = models.CharField(max_length=128, blank=True, null=True, verbose_name=_('Hostname raw'))
labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels"))
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
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'))
objects = OrgManager.from_queryset(AssetQuerySet)()
CONNECTIVITY_CACHE_KEY = '_JMS_ASSET_CONNECTIVITY_{}'
UNREACHABLE, REACHABLE, UNKNOWN = range(0, 3)
CONNECTIVITY_CHOICES = (
(UNREACHABLE, _("Unreachable")),
(REACHABLE, _('Reachable')),
(UNKNOWN, _("Unknown")),
)
def __str__(self):
return '{0.hostname}({0.ip})'.format(self)
......@@ -197,25 +181,17 @@ class Asset(OrgModelMixin):
return ''
@property
def is_connective(self):
def connectivity(self):
if not self.is_unixlike():
return True
val = cache.get(ASSET_ADMIN_CONN_CACHE_KEY.format(self.hostname))
if val == 1:
return True
else:
return False
return self.UNKNOWN
key = self.CONNECTIVITY_CACHE_KEY.format(str(self.id))
cached = cache.get(key, None)
return cached if cached is not None else self.UNKNOWN
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
@connectivity.setter
def connectivity(self, value):
key = self.CONNECTIVITY_CACHE_KEY.format(str(self.id))
cache.set(key, value, 3600*2)
def get_auth_info(self):
if self.admin_user:
......@@ -236,11 +212,20 @@ class Asset(OrgModelMixin):
fake_node.is_node = False
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, First using asset user,
otherwise using cluster admin user
Ansible use it create inventory
Todo: May be move to ops implements it
"""
data = self.to_json()
......
......@@ -29,6 +29,13 @@ class AssetUser(OrgModelMixin):
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
UNREACHABLE, REACHABLE, UNKNOWN = range(0, 3)
CONNECTIVITY_CHOICES = (
(UNREACHABLE, _("Unreachable")),
(REACHABLE, _('Reachable')),
(UNKNOWN, _("Unknown")),
)
@property
def password(self):
if self._password:
......
......@@ -14,7 +14,7 @@ from ..const import SYSTEM_USER_CONN_CACHE_KEY
from .base import AssetUser
__all__ = ['AdminUser', 'SystemUser',]
__all__ = ['AdminUser', 'SystemUser']
logger = logging.getLogger(__name__)
signer = get_signer()
......@@ -31,6 +31,7 @@ class AdminUser(AssetUser):
become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4)
become_user = models.CharField(default='root', max_length=64)
_become_pass = models.CharField(default='', max_length=128)
CONNECTIVE_CACHE_KEY = '_JMS_ADMIN_USER_CONNECTIVE_{}'
def __str__(self):
return self.name
......@@ -67,6 +68,23 @@ class AdminUser(AssetUser):
def assets_amount(self):
return self.get_related_assets().count()
@property
def connectivity(self):
from .asset import Asset
assets = self.get_related_assets().values_list('id', 'hostname', flat=True)
data = {
'unreachable': [],
'reachable': [],
}
for asset_id, hostname in assets:
key = Asset.CONNECTIVITY_CACHE_KEY.format(str(self.id))
value = cache.get(key, Asset.UNKNOWN)
if value == Asset.REACHABLE:
data['reachable'].append(hostname)
elif value == Asset.UNREACHABLE:
data['unreachable'].append(hostname)
return data
class Meta:
ordering = ['name']
unique_together = [('name', 'org_id')]
......@@ -94,34 +112,34 @@ class AdminUser(AssetUser):
class SystemUser(AssetUser):
SSH_PROTOCOL = 'ssh'
RDP_PROTOCOL = 'rdp'
TELNET_PROTOCOL = 'telnet'
PROTOCOL_SSH = 'ssh'
PROTOCOL_RDP = 'rdp'
PROTOCOL_TELNET = 'telnet'
PROTOCOL_CHOICES = (
(SSH_PROTOCOL, 'ssh'),
(RDP_PROTOCOL, 'rdp'),
(TELNET_PROTOCOL, 'telnet (beta)'),
(PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'),
(PROTOCOL_TELNET, 'telnet (beta)'),
)
AUTO_LOGIN = 'auto'
MANUAL_LOGIN = 'manual'
LOGIN_AUTO = 'auto'
LOGIN_MANUAL = 'manual'
LOGIN_MODE_CHOICES = (
(AUTO_LOGIN, _('Automatic login')),
(MANUAL_LOGIN, _('Manually login'))
(LOGIN_AUTO, _('Automatic login')),
(LOGIN_MANUAL, _('Manually login'))
)
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
priority = models.IntegerField(default=20, verbose_name=_("Priority"),
validators=[MinValueValidator(1), MaxValueValidator(100)])
priority = models.IntegerField(default=20, verbose_name=_("Priority"), validators=[MinValueValidator(1), MaxValueValidator(100)])
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=AUTO_LOGIN, max_length=10, verbose_name=_('Login mode'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
cmd_filters = models.ManyToManyField('CommandFilter', related_name='system_users', verbose_name=_("Command filter"), blank=True)
cache_key = "__SYSTEM_USER_CACHED_{}"
SYSTEM_USER_CACHE_KEY = "__SYSTEM_USER_CACHED_{}"
CONNECTIVE_CACHE_KEY = '_JMS_SYSTEM_USER_CONNECTIVE_{}'
def __str__(self):
return '{0.name}({0.username})'.format(self)
......@@ -136,34 +154,61 @@ class SystemUser(AssetUser):
'auto_push': self.auto_push,
}
def get_assets(self):
def get_related_assets(self):
assets = set(self.assets.all())
return assets
@property
def assets_connective(self):
_result = cache.get(SYSTEM_USER_CONN_CACHE_KEY.format(self.name), {})
return _result
def connectivity(self):
cache_key = self.CONNECTIVE_CACHE_KEY.format(str(self.id))
value = cache.get(cache_key, None)
if not value or 'unreachable' not in value:
return {'unreachable': [], 'reachable': []}
else:
return value
@connectivity.setter
def connectivity(self, value):
data = self.connectivity
unreachable = data['unreachable']
reachable = data['reachable']
for host in value.get('dark', {}).keys():
if host not in unreachable:
unreachable.append(host)
if host in reachable:
reachable.remove(host)
for host in value.get('contacted'):
if host not in reachable:
reachable.append(host)
if host in unreachable:
unreachable.remove(host)
cache_key = self.CONNECTIVE_CACHE_KEY.format(str(self.id))
cache.set(cache_key, data, 3600)
@property
def assets_unreachable(self):
return self.connectivity.get('unreachable')
@property
def unreachable_assets(self):
return list(self.assets_connective.get('dark', {}).keys())
def assets_reachable(self):
return self.connectivity.get('reachable')
@property
def reachable_assets(self):
return self.assets_connective.get('contacted', [])
def login_mode_display(self):
return self.get_login_mode_display()
def is_need_push(self):
if self.auto_push and self.protocol == self.__class__.SSH_PROTOCOL:
if self.auto_push and self.protocol == self.PROTOCOL_SSH:
return True
else:
return False
def set_cache(self):
cache.set(self.cache_key.format(self.id), self, 3600)
cache.set(self.SYSTEM_USER_CACHE_KEY.format(self.id), self, 3600)
def expire_cache(self):
cache.delete(self.cache_key.format(self.id))
cache.delete(self.SYSTEM_USER_CACHE_KEY.format(self.id))
@property
def cmd_filter_rules(self):
......@@ -184,7 +229,7 @@ class SystemUser(AssetUser):
@classmethod
def get_system_user_by_id_or_cached(cls, sid):
cached = cache.get(cls.cache_key.format(sid))
cached = cache.get(cls.SYSTEM_USER_CACHE_KEY.format(sid))
if cached:
return cached
try:
......
......@@ -9,7 +9,7 @@ from .system_user import AssetSystemUserSerializer
__all__ = [
'AssetSerializer', 'AssetGrantedSerializer', 'MyAssetGrantedSerializer',
'AssetAsNodeSerializer',
'AssetAsNodeSerializer', 'AssetSimpleSerializer',
]
......@@ -33,7 +33,7 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info)
fields.extend([
'hardware_info', 'is_connective', 'org_name'
'hardware_info', 'connectivity', 'org_name'
])
return fields
......@@ -78,3 +78,9 @@ class MyAssetGrantedSerializer(AssetGrantedSerializer):
"is_active", "system_users_join", "org_name",
"os", "platform", "comment", "org_id", "protocol"
)
class AssetSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ['id', 'hostname', 'port', 'ip', 'connectivity']
from rest_framework import serializers
from ..models import SystemUser
from ..models import SystemUser, Asset
from .base import AuthSerializer
......@@ -21,17 +21,17 @@ class SystemUserSerializer(serializers.ModelSerializer):
def get_field_names(self, declared_fields, info):
fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info)
fields.extend([
'get_login_mode_display',
'login_mode_display',
])
return fields
@staticmethod
def get_unreachable_assets(obj):
return obj.unreachable_assets
return obj.assets_unreachable
@staticmethod
def get_reachable_assets(obj):
return obj.reachable_assets
return obj.assets_reachable
def get_unreachable_amount(self, obj):
return len(self.get_unreachable_assets(obj))
......@@ -41,7 +41,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
@staticmethod
def get_assets_amount(obj):
return len(obj.get_assets())
return len(obj.get_related_assets())
class SystemUserAuthSerializer(AuthSerializer):
......@@ -75,4 +75,7 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer):
"""
class Meta:
model = SystemUser
fields = ('id', 'name', 'username')
\ No newline at end of file
fields = ('id', 'name', 'username')
......@@ -7,7 +7,7 @@ from django.dispatch import receiver
from common.utils import get_logger
from .models import Asset, SystemUser, Node
from .tasks import update_assets_hardware_info_util, \
test_asset_connectability_util, push_system_user_to_assets
test_asset_connectivity_util, push_system_user_to_assets
logger = get_logger(__file__)
......@@ -19,8 +19,8 @@ def update_asset_hardware_info_on_created(asset):
def test_asset_conn_on_created(asset):
logger.debug("Test asset `{}` connectability".format(asset))
test_asset_connectability_util.delay([asset])
logger.debug("Test asset `{}` connectivity".format(asset))
test_asset_connectivity_util.delay([asset])
def set_asset_root_node(asset):
......
This diff is collapsed.
......@@ -45,13 +45,11 @@
<table class="table table-striped table-bordered table-hover" id="asset_list_table">
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Reachable' %}</th>
<th>{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
......@@ -91,26 +89,36 @@
<script>
function initTable() {
var reachable = {{ admin_user.REACHABLE }};
var unreachable = {{ admin_user.UNREACHABLE }};
var options = {
ele: $('#asset_list_table'),
buttons: [],
order: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
{targets: 0, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-detail" pk=DEFAULT_PK %}" data-aid="'+rowData.id+'">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData) {
if (!cellData) {
{targets: 3, createdCell: function (td, cellData) {
if (cellData === unreachable) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
} else if (cellData === reachable) {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}}],
ajax_url: '{% url "api-assets:asset-list" %}?admin_user_id={{ admin_user.id }}',
} else {
$(td).html('')
}
}},
{targets: 4, createdCell: function (td, cellData) {
var test_btn = ' <a class="btn btn-xs btn-info btn-test-asset" data-uid="{{ DEFAULT_PK }}" >{% trans "Test" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
$(td).html(test_btn);
}}
],
ajax_url: '{% url "api-assets:admin-user-assets" pk=admin_user.id %}',
columns: [
{data: function(){return ""}}, {data: "hostname" }, {data: "ip" },
{data: "port" }, {data: "is_connective" }],
{data: "hostname" }, {data: "ip" },
{data: "port" }, {data: "connectivity" }, {data: "id"}],
op_html: $('#actions').html()
};
jumpserver.initServerSideDataTable(options);
......@@ -119,6 +127,21 @@ function initTable() {
$(document).ready(function () {
initTable();
})
.on('click', '.btn-test-asset', function () {
var asset_id = $(this).data('uid');
var the_url = "{% url 'api-assets:asset-alive-test' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', asset_id);
var success = function (data) {
var task_id = data.task;
var url = '{% url "ops:celery-task-log" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", task_id);
window.open(url, '', 'width=800,height=600,left=400,top=400')
};
APIUpdateAttr({
url: the_url,
method: 'GET',
success: success,
flash_message: false
});
})
.on('click', '.btn-test-connective', function () {
var the_url = "{% url 'api-assets:admin-user-connective' pk=admin_user.id %}";
var success = function (data) {
......
......@@ -136,7 +136,7 @@
{% block custom_foot_js %}
<script>
function initAssetsTable() {
var unreachable = {{ system_user.unreachable_assets|safe }};
var connectivity = {{ system_user.connectivity | safe }};
var options = {
ele: $('#system_user_list'),
buttons: [],
......@@ -147,11 +147,13 @@ function initAssetsTable() {
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 3, createdCell: function (td, cellData) {
if (unreachable.indexOf(cellData) >= 0) {
if (connectivity.unreachable.indexOf(cellData) >= 0) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
} else if (connectivity.reachable.indexOf(cellData) >= 0 ) {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
} else {
$(td).html('')
}
}},
{targets: 4, createdCell: function (td, cellData) {
var push_btn = '';
......
......@@ -95,7 +95,7 @@ function initTable() {
}}],
ajax_url: '{% url "api-assets:system-user-list" %}',
columns: [
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "get_login_mode_display"}, {data: "assets_amount" },
{data: "id" }, {data: "name" }, {data: "username" }, {data: "protocol"}, {data: "login_mode_display"}, {data: "assets_amount" },
{data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" }
],
op_html: $('#actions').html()
......
......@@ -24,32 +24,34 @@ cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-r
urlpatterns = [
path('assets-bulk/', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
path('system-user/<uuid:pk>/auth-info/',
api.SystemUserAuthInfoApi.as_view(), name='system-user-auth-info'),
path('system-user/<uuid:pk>/assets/',
api.SystemUserAssetsListView.as_view(), name='system-user-assets'),
path('assets/<uuid:pk>/refresh/',
api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'),
path('assets/<uuid:pk>/alive/',
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
path('assets/<uuid:pk>/gateway/',
api.AssetGatewayApi.as_view(), name='asset-gateway'),
path('admin-user/<uuid:pk>/nodes/',
api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'),
path('admin-user/<uuid:pk>/auth/',
api.AdminUserAuthApi.as_view(), name='admin-user-auth'),
path('admin-user/<uuid:pk>/connective/',
api.AdminUserTestConnectiveApi.as_view(), name='admin-user-connective'),
path('admin-user/<uuid:pk>/assets/',
api.AdminUserAssetsListView.as_view(), name='admin-user-assets'),
path('system-user/<uuid:pk>/auth-info/',
api.SystemUserAuthInfoApi.as_view(), name='system-user-auth-info'),
path('system-user/<uuid:pk>/assets/',
api.SystemUserAssetsListView.as_view(), name='system-user-assets'),
path('system-user/<uuid:pk>/push/',
api.SystemUserPushApi.as_view(), name='system-user-push'),
path('system-user/<uuid:pk>/asset/<uuid:aid>/push/',
api.SystemUserPushToAssetApi.as_view(), name='system-user-push-to-asset'),
path('system-user/<uuid:pk>/asset/<uuid:aid>/test/',
api.SystemUserTestAssetConnectabilityApi.as_view(), name='system-user-test-to-asset'),
api.SystemUserTestAssetConnectivityApi.as_view(), name='system-user-test-to-asset'),
path('system-user/<uuid:pk>/connective/',
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
path('system-user/<uuid:pk>/cmd-filter-rules/',
api.SystemUserCommandFilterRuleListApi.as_view(), name='system-user-cmd-filter-rule-list'),
......
......@@ -102,7 +102,7 @@ class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
'app': _('Assets'),
'action': _('Admin user detail'),
"total_amount": len(self.queryset),
'unreachable_amount': len([asset for asset in self.queryset if asset.is_connective is False])
'unreachable_amount': len([asset for asset in self.queryset if asset.connectivity is False])
}
kwargs.update(context)
return super().get_context_data(**kwargs)
......
......@@ -304,7 +304,7 @@ defaults = {
'REDIS_DB_CELERY': 3,
'REDIS_DB_CACHE': 4,
'CAPTCHA_TEST_MODE': None,
'TOKEN_EXPIRATION': 3600,
'TOKEN_EXPIRATION': 3600 * 24,
'DISPLAY_PER_PAGE': 25,
'DEFAULT_EXPIRED_YEARS': 70,
'SESSION_COOKIE_DOMAIN': None,
......@@ -312,6 +312,7 @@ defaults = {
'SESSION_COOKIE_AGE': 3600 * 24,
'SESSION_EXPIRE_AT_BROWSER_CLOSE': False,
'AUTH_OPENID': False,
'OTP_ISSUER_NAME': 'Jumpserver',
'EMAIL_SUFFIX': 'jumpserver.org',
'TERMINAL_PASSWORD_AUTH': True,
'TERMINAL_PUBLIC_KEY_AUTH': True,
......
......@@ -637,7 +637,7 @@ msgstr "分类"
#: assets/models/node.py:20
msgid "Key"
msgstr ""
msgstr ""
#: assets/models/node.py:127
msgid "New node"
......@@ -739,15 +739,15 @@ msgid "Update asset hardware info: {}"
msgstr "更新资产硬件信息: {}"
#: assets/tasks.py:230
msgid "Test admin user connectability period: {}"
msgid "Test admin user connectivity period: {}"
msgstr "定期测试管理账号可连接性: {}"
#: assets/tasks.py:236
msgid "Test admin user connectability: {}"
msgid "Test admin user connectivity: {}"
msgstr "测试管理行号可连接性: {}"
#: assets/tasks.py:246
msgid "Test assets connectability"
msgid "Test assets connectivity"
msgstr "测试资产可连接性"
#: assets/tasks.py:251 assets/tasks.py:316 assets/tasks.py:423
......@@ -763,15 +763,15 @@ msgid "No assets, task stop"
msgstr "没有匹配到资产,结束任务"
#: assets/tasks.py:280
msgid "Test assets connectability: {}"
msgid "Test assets connectivity: {}"
msgstr "测试资产可连接性: {}"
#: assets/tasks.py:339
msgid "Test system user connectability: {}"
msgid "Test system user connectivity: {}"
msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:346
msgid "Test system user connectability: {} => {}"
msgid "Test system user connectivity: {} => {}"
msgstr "测试系统用户可连接性: {} => {}"
#: assets/tasks.py:414
......@@ -4508,7 +4508,7 @@ msgstr "创建账户"
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:91
msgid "Loading..."
msgstr ""
msgstr "加载中..."
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:106
msgid "Load failed"
......@@ -4634,7 +4634,7 @@ msgstr "更新组织"
#~ msgid "Update assets hardware info period"
#~ msgstr "定期更新资产硬件信息"
#~ msgid "Test system user connectability period: {}"
#~ msgid "Test system user connectivity period: {}"
#~ msgstr "定期测试系统用户可连接性: {}"
#~ msgid "Date finished"
......
......@@ -24,8 +24,10 @@ class TaskViewSet(viewsets.ModelViewSet):
def get_queryset(self):
queryset = super().get_queryset()
if current_org:
if current_org.is_real():
queryset = queryset.filter(created_by=current_org.id)
else:
queryset = queryset.filter(created_by='')
return queryset
......
......@@ -27,7 +27,7 @@ class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
def get_queryset(self):
queryset = super().get_queryset()
if current_org:
if current_org.is_real():
queryset = queryset.filter(created_by=current_org.id)
else:
queryset = queryset.filter(created_by='')
......
......@@ -46,6 +46,9 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet):
self.permission_classes = (IsOrgAdminOrAppUser,)
return super().get_permissions()
def allow_bulk_destroy(self, qs, filtered):
return qs.count() == filtered.count()
class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsOrgAdmin,)
......
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