diff --git a/apps/assets/api.py b/apps/assets/api.py index ab3507b3d..860f7111c 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -18,14 +18,16 @@ from rest_framework.response import Response from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView from django.shortcuts import get_object_or_404 +from django.db.models import Q from common.mixins import IDInFilterMixin -from common.utils import get_object_or_none from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \ - get_user_granted_assets, push_users + get_user_granted_assets from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser from . import serializers -from .tasks import update_assets_hardware_info, test_admin_user_connectability_manual +from .tasks import update_assets_hardware_info, test_admin_user_connectability, \ + test_admin_user_connectability_manual, push_system_user_to_cluster_assets, \ + test_system_user_connectability class AssetViewSet(IDInFilterMixin, BulkModelViewSet): @@ -51,8 +53,9 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet): queryset = queryset.filter(groups__id=asset_group_id) if admin_user_id: admin_user = get_object_or_404(AdminUser, id=admin_user_id) + assets_direct = [asset.id for asset in admin_user.asset_set.all()] clusters = [cluster.id for cluster in admin_user.cluster_set.all()] - queryset = queryset.filter(cluster__id__in=clusters) + queryset = queryset.filter(Q(cluster__id__in=clusters)|Q(id__in=assets_direct)) return queryset @@ -65,15 +68,6 @@ class AssetGroupViewSet(IDInFilterMixin, BulkModelViewSet): permission_classes = (IsSuperUser,) -class AssetUpdateGroupApi(generics.RetrieveUpdateAPIView): - """ - Asset update it's group api - """ - queryset = Asset.objects.all() - serializer_class = serializers.AssetUpdateGroupSerializer - permission_classes = (IsSuperUser,) - - class GroupUpdateAssetsApi(generics.RetrieveUpdateAPIView): """ Asset group, update it's asset member @@ -117,6 +111,18 @@ class ClusterViewSet(IDInFilterMixin, BulkModelViewSet): permission_classes = (IsSuperUser,) +# TOdo +class ClusterTestAssetsAliveApi(generics.RetrieveAPIView): + """ + Test cluster asset can connect using admin user or not + """ + queryset = Cluster.objects.all() + permission_classes = (IsSuperUser,) + + def retrieve(self, request, *args, **kwargs): + cluster = self.get_object() + + class ClusterAddAssetsApi(generics.UpdateAPIView): queryset = Cluster.objects.all() serializer_class = serializers.ClusterUpdateAssetsSerializer @@ -162,7 +168,7 @@ class AdminUserAddClustersApi(generics.UpdateAPIView): return Response({'error': serializer.errors}, status=400) -class SystemUserViewSet(IDInFilterMixin, BulkModelViewSet): +class SystemUserViewSet(BulkModelViewSet): """ System user api set, for add,delete,update,list,retrieve resource """ @@ -170,6 +176,10 @@ class SystemUserViewSet(IDInFilterMixin, BulkModelViewSet): serializer_class = serializers.SystemUserSerializer permission_classes = (IsSuperUserOrAppUser,) + def update(self, request, *args, **kwargs): + print(request.data) + return super().update(request, *args, **kwargs) + class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): """ @@ -199,7 +209,7 @@ class SystemUserAuthInfoApi(generics.RetrieveAPIView): return Response(data) -class AssetRefreshHardwareView(generics.RetrieveAPIView): +class AssetRefreshHardwareApi(generics.RetrieveAPIView): """ Refresh asset hardware info """ @@ -217,7 +227,7 @@ class AssetRefreshHardwareView(generics.RetrieveAPIView): return Response({"msg": "ok"}) -class AssetAdminUserTestView(AssetRefreshHardwareView): +class AssetAdminUserTestApi(generics.RetrieveAPIView): """ Test asset admin user connectivity """ @@ -227,9 +237,47 @@ class AssetAdminUserTestView(AssetRefreshHardwareView): def retrieve(self, request, *args, **kwargs): asset_id = kwargs.get('pk') asset = get_object_or_404(Asset, pk=asset_id) - result = test_admin_user_connectability_manual(asset) - if result: - return Response('1') + ok, msg = test_admin_user_connectability_manual(asset) + if ok: + return Response({"msg": "pong"}) else: - return Response('0', status=502) + return Response({"error": msg}, status=502) + +class AdminUserTestConnectiveApi(generics.RetrieveAPIView): + """ + Test asset admin user connectivity + """ + queryset = AdminUser.objects.all() + permission_classes = (IsSuperUser,) + + def retrieve(self, request, *args, **kwargs): + admin_user = self.get_object() + test_admin_user_connectability.delay(admin_user, force=True) + return Response({"msg": "Task created"}) + + +class SystemUserPushApi(generics.RetrieveAPIView): + """ + Push system user to cluster assets api + """ + queryset = SystemUser.objects.all() + permission_classes = (IsSuperUser,) + + def retrieve(self, request, *args, **kwargs): + system_user = self.get_object() + push_system_user_to_cluster_assets.delay(system_user, force=True) + return Response({"msg": "Task created"}) + + +class SystemUserTestConnectiveApi(generics.RetrieveAPIView): + """ + Push system user to cluster assets api + """ + queryset = SystemUser.objects.all() + permission_classes = (IsSuperUser,) + + def retrieve(self, request, *args, **kwargs): + system_user = self.get_object() + test_system_user_connectability.delay(system_user, force=True) + return Response({"msg": "Task created"}) diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 2b701dcec..2bf6df507 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -273,26 +273,24 @@ class SystemUserForm(forms.ModelForm): } -class SystemUserUpdateForm(forms.ModelForm): - class Meta: - model = SystemUser - fields = [ - 'name', 'username', 'protocol', 'priority', - 'sudo', 'comment', 'shell', 'cluster' - ] - widgets = { - 'name': forms.TextInput(attrs={'placeholder': _('Name')}), - 'username': forms.TextInput(attrs={'placeholder': _('Username')}), - 'cluster': forms.SelectMultiple( - attrs={'class': 'select2', - 'data-placeholder': _(' Select clusters')}), - } - help_texts = { - 'name': '* required', - 'username': '* required', - 'cluster': 'If auto push checked, then push system user to that cluster assets', - 'priority': 'High level will be using login asset as default, if user was granted more than 2 system user', - } +class SystemUserUpdateForm(SystemUserForm): + def save(self, commit=True): + # Because we define custom field, so we need rewrite :method: `save` + password = self.cleaned_data.get('password', None) + private_key_file = self.cleaned_data.get('private_key_file') + system_user = super(forms.ModelForm, self).save() + + if private_key_file: + print(private_key_file) + private_key = private_key_file.read().strip().decode('utf-8') + public_key = ssh_pubkey_gen(private_key=private_key) + else: + private_key = public_key = None + system_user.set_auth(password=password, private_key=private_key, public_key=public_key) + return system_user + + def clean_password(self): + return self.cleaned_data['password'] class SystemUserAuthForm(forms.Form): diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index f606870de..905cee012 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -116,6 +116,23 @@ class Asset(models.Model): else: return False + @property + def admin_user_avail(self): + if self.admin_user: + admin_user = self.admin_user + elif self.cluster and self.cluster.admin_user: + admin_user = self.cluster.admin_user + else: + return None + return admin_user + + @property + def is_has_private_admin_user(self): + if self.admin_user: + return True + else: + return False + def to_json(self): return { 'id': self.id, @@ -133,12 +150,8 @@ class Asset(models.Model): Todo: May be move to ops implements it """ data = self.to_json() - admin_user = None - if self.admin_user: - admin_user = self.admin_user - elif self.cluster and self.cluster.admin_user: - admin_user = self.cluster.admin_user - if admin_user: + if self.admin_user_avail: + admin_user = self.admin_user_avail data.update({ 'username': admin_user.username, 'password': admin_user.password, diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 03fcf3068..c9db83e27 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -8,12 +8,14 @@ import uuid from hashlib import md5 import sshpubkeys +from django.core.cache import cache from django.db import models from django.utils.translation import ugettext_lazy as _ from django.conf import settings from common.utils import signer, ssh_key_string_to_obj, ssh_key_gen from .utils import private_key_validator +from ..const import SYSTEM_USER_CONN_CACHE_KEY __all__ = ['AdminUser', 'SystemUser',] @@ -103,6 +105,7 @@ class AssetUser(models.Model): update_fields.append('_public_key') if update_fields: + print(update_fields) self.save(update_fields=update_fields) def auto_gen_auth(self): @@ -195,7 +198,7 @@ class SystemUser(AssetUser): ('P', 'Password'), ('K', 'Public key'), ) - cluster = models.ManyToManyField('assets.Cluster', verbose_name=_("Cluster")) + cluster = models.ManyToManyField('assets.Cluster', null=True, blank=True, verbose_name=_("Cluster")) priority = models.IntegerField(default=10, verbose_name=_("Priority")) # Todo: If user granted more priority user, default will be login as the hign protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) @@ -224,6 +227,19 @@ class SystemUser(AssetUser): 'auto_push': self.auto_push, } + @property + def assets_connective(self): + _result = cache.get(SYSTEM_USER_CONN_CACHE_KEY.format(self.name), {}) + return _result + + @property + def unreachable_assets(self): + return list(self.assets_connective.get('dark', {}).keys()) + + @property + def reachable_assets(self): + return self.assets_connective.get('contacted', []) + class Meta: ordering = ['name'] diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index be54089cb..f13f7837d 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -25,17 +25,6 @@ class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): return obj.assets.count() -class AssetUpdateGroupSerializer(serializers.ModelSerializer): - """ - 资产更新自己所在资产组的请求数据结构定义 - """ - groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all()) - - class Meta: - model = Asset - fields = ['id', 'groups'] - - class AssetUpdateSystemUserSerializer(serializers.ModelSerializer): """ 资产更新其系统用户请求的数据结构定义 @@ -90,11 +79,7 @@ class AdminUserSerializer(serializers.ModelSerializer): @staticmethod def get_assets_amount(obj): - amount = 0 - clusters = obj.cluster_set.all() - for cluster in clusters: - amount += len(cluster.assets.all()) - return amount + return obj.assets_amount class SystemUserSerializer(serializers.ModelSerializer): @@ -102,6 +87,9 @@ class SystemUserSerializer(serializers.ModelSerializer): 系统用户 """ unreachable_amount = serializers.SerializerMethodField() + reachable_amount = serializers.SerializerMethodField() + unreachable_assets = serializers.SerializerMethodField() + reachable_assets = serializers.SerializerMethodField() assets_amount = serializers.SerializerMethodField() class Meta: @@ -109,12 +97,18 @@ class SystemUserSerializer(serializers.ModelSerializer): exclude = ('_password', '_private_key', '_public_key') @staticmethod - def get_unreachable_amount(obj): - data = cache.get(SYSTEM_USER_CONN_CACHE_KEY.format(obj.name)) - if data: - return len(data.get('dark')) - else: - return "Unknown" + def get_unreachable_assets(obj): + return obj.unreachable_assets + + @staticmethod + def get_reachable_assets(obj): + return obj.reachable_assets + + def get_unreachable_amount(self, obj): + return len(self.get_unreachable_assets(obj)) + + def get_reachable_amount(self, obj): + return len(self.get_reachable_assets(obj)) @staticmethod def get_assets_amount(obj): diff --git a/apps/assets/tasks.py b/apps/assets/tasks.py index 3370470df..09430da96 100644 --- a/apps/assets/tasks.py +++ b/apps/assets/tasks.py @@ -181,10 +181,10 @@ def test_admin_user_connectability_manual(asset, task_name=None): if result.results_summary['dark']: cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(asset.hostname), 0, CACHE_MAX_TIME) - return False + return False, result.results_summary['dark'] else: cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(asset.hostname), 1, CACHE_MAX_TIME) - return True + return True, "" @shared_task @@ -211,7 +211,8 @@ def test_system_user_connectability(system_user, force=False): ) cache.set(lock_key, 1, CACHE_MAX_TIME) result = task.run() - cache_key = const.SYSTEM_USER_CONN_CACHE_KEY + cache_key = const.SYSTEM_USER_CONN_CACHE_KEY.format(system_user.name) + print("Set cache: {} {}".format(cache_key, result.results_summary)) cache.set(cache_key, result.results_summary, CACHE_MAX_TIME) return result.results_summary @@ -369,7 +370,7 @@ def update_asset_info_when_created(sender, instance=None, created=False, **kwarg @receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier") -def update_asset_conn_info_when_created(sender, instance=None, created=False, **kwargs): +def update_asset_conn_info_on_created(sender, instance=None, created=False, **kwargs): if instance and created: task_name = 'TEST-ASSET-CONN-WHEN-CREATED-{}'.format(instance) msg = "Receive asset {} create signal, test asset connectability".format( @@ -380,7 +381,7 @@ def update_asset_conn_info_when_created(sender, instance=None, created=False, ** @receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier") -def push_system_user_when_created(sender, instance=None, created=False, **kwargs): +def push_system_user_on_created(sender, instance=None, created=False, **kwargs): if instance and created: task_name = 'PUSH-SYSTEM-USER-WHEN-ASSET-CREATED-{}'.format(instance) system_users = instance.cluster.systemuser_set.all() @@ -392,15 +393,7 @@ def push_system_user_when_created(sender, instance=None, created=False, **kwargs @receiver(post_save, sender=SystemUser) -def push_system_user_on_change(sender, instance=None, created=False, **kwargs): - if instance and instance.auto_push: - logger.debug("System user `{}` auth changed, push it".format(instance.name)) - task_name = "PUSH-SYSTEM-USER-ON-CREATED-{}".format(instance.name) - push_system_user_to_cluster_assets.delay(instance, task_name) - - -@receiver(post_save, sender=SystemUser) -def push_system_user_on_change(sender, instance=None, update_fields=None, **kwargs): +def push_system_user_on_auth_change(sender, instance=None, update_fields=None, **kwargs): fields_check = {'_password', '_private_key', '_public_key'} auth_changed = update_fields & fields_check if update_fields else None if instance and instance.auto_push and auth_changed: diff --git a/apps/assets/templates/assets/admin_user_assets.html b/apps/assets/templates/assets/admin_user_assets.html index b1698c976..4b0429605 100644 --- a/apps/assets/templates/assets/admin_user_assets.html +++ b/apps/assets/templates/assets/admin_user_assets.html @@ -60,7 +60,7 @@ {% trans 'IP' %} {% trans 'Port' %} {% trans 'Type' %} - {% trans 'Valid' %} + {% trans 'Alive' %} @@ -78,10 +78,10 @@ - + @@ -98,56 +98,6 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/assets/templates/assets/admin_user_detail.html b/apps/assets/templates/assets/admin_user_detail.html index 5f0646197..577f16402 100644 --- a/apps/assets/templates/assets/admin_user_detail.html +++ b/apps/assets/templates/assets/admin_user_detail.html @@ -31,7 +31,7 @@
-
+
{{ admin_user.name }} @@ -77,26 +77,8 @@
-
-
-
- {% trans 'Quick update' %} -
-
-
{% trans 'Retest connectivity' %}:{% trans 'Test connective' %}: - +
- - - - - - -
{% trans 'Test auth all assets manual' %}: - - - -
- - +
+
{% trans 'Using this as cluster admin user' %} diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html index 44e5bb547..080f701f8 100644 --- a/apps/assets/templates/assets/asset_detail.html +++ b/apps/assets/templates/assets/asset_detail.html @@ -71,8 +71,8 @@ {% trans 'Admin user' %}: - {% if asset.admin_user %} - {{ asset.admin_user.name }} + {% if asset.admin_user_avail %} + {{ asset.admin_user_avail.name }} {% else %} None {% endif %} @@ -195,6 +195,14 @@ + + {% trans 'Test is alive' %}: + + + + + +
@@ -248,7 +256,7 @@ diff --git a/apps/assets/templates/assets/cluster_assets.html b/apps/assets/templates/assets/cluster_assets.html index 16a3909b0..13d779d8f 100644 --- a/apps/assets/templates/assets/cluster_assets.html +++ b/apps/assets/templates/assets/cluster_assets.html @@ -64,6 +64,25 @@
+
+ {% trans 'Quick update' %} +
+
+ + + + + + + +
{% trans 'Test assets alive' %}: + + + +
+
+
+
{% trans 'Add assets to' %} {{ cluster.name }}
@@ -173,7 +192,6 @@ $(document).ready(function () { }); initTable(); }) - .on('click', '.btn-add-assets', function () { if (Object.keys(jumpserver.assets_selected).length === 0) { return false; @@ -185,5 +203,8 @@ $(document).ready(function () { addAssets(assets_id); }) +.on('click', '#btn-test-assets', function () { + console.log('ok'); +}) {% endblock %} diff --git a/apps/assets/templates/assets/cluster_detail.html b/apps/assets/templates/assets/cluster_detail.html index 19ab7c927..cd2ecd25e 100644 --- a/apps/assets/templates/assets/cluster_detail.html +++ b/apps/assets/templates/assets/cluster_detail.html @@ -102,6 +102,44 @@
+{#
#} +{#
#} +{#
#} +{# {% trans 'Update admin user' %}#} +{#
#} +{#
#} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{# #} +{##} +{# {% for cluster in system_user.cluster.all %}#} +{# #} +{# #} +{# #} +{# #} +{# {% endfor %}#} +{# #} +{#
#} +{# #} +{#
#} +{# #} +{#
{{ cluster.name }}#} +{# #} +{#
#} +{#
#} +{#
#} +{#
#} @@ -111,46 +149,16 @@ {% endblock %} {% block custom_foot_js %} {% endblock %} diff --git a/apps/assets/templates/assets/system_user_asset.html b/apps/assets/templates/assets/system_user_asset.html index edda1c222..75e1b264d 100644 --- a/apps/assets/templates/assets/system_user_asset.html +++ b/apps/assets/templates/assets/system_user_asset.html @@ -16,11 +16,6 @@
  • {% trans 'Detail' %}
  • -
  • - - {% trans 'Auth' %} - -
  • {% trans 'Assets' %} @@ -78,15 +73,15 @@ {% trans 'Push system user manually' %}: - + - {% trans 'Refresh assets connectivity' %}: + {% trans 'Test assets connective' %}: - + @@ -103,26 +98,8 @@ {% endblock %} {% block custom_foot_js %} + // clear jumpserver.groups_selected + jumpserver.cluster_selected = {}; + }; + APIUpdateAttr({ + url: the_url, + body: JSON.stringify(body), + success: success + }); +} +jumpserver.cluster_selected = {}; +$(document).ready(function () { + $('.select2').select2() + .on('select2:select', function(evt) { + var data = evt.params.data; + jumpserver.cluster_selected[data.id] = data.text; + }) + .on('select2:unselect', function(evt) { + var data = evt.params.data; + delete jumpserver.cluster_selected[data.id]; + }); +}) +.on('click', '#btn-auto-push', function () { + var checked = $(this).prop('checked'); + var the_url = "{% url 'api-assets:system-user-detail' pk=system_user.id %}"; + var body = { + 'auto_push': checked + }; + APIUpdateAttr({ + url: the_url, + body: JSON.stringify(body) + }); +}) +.on('click', '#btn-add-to-cluster', function() { + if (Object.keys(jumpserver.cluster_selected).length === 0) { + return false; + } + var clusters = $('.bdg_cluster').map(function() { + return $(this).data('gid'); + }).get(); + $.map(jumpserver.cluster_selected, function(value, index) { + clusters.push(index); + }); + updateSystemUserCluster(clusters); +}) +.on('click', '.btn-remove-from-cluster', function() { + var $this = $(this); + var $tr = $this.closest('tr'); + var $badge = $tr.find('.bdg_cluster'); + var gid = $badge.data('gid'); + var cluster_name = $badge.html() || $badge.text(); + $('#groups_selected').append( + '' + ); + $tr.remove(); + var clusters = $('.bdg_cluster').map(function () { + return $(this).data('gid'); + }).get(); + updateSystemUserCluster(clusters); +}) + {% endblock %} diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html index 6aa32a622..c200b9250 100644 --- a/apps/assets/templates/assets/system_user_list.html +++ b/apps/assets/templates/assets/system_user_list.html @@ -17,7 +17,9 @@ {% trans 'Name' %} {% trans 'Username' %} {% trans 'Asset' %} + {% trans 'Reachable' %} {% trans 'Unreachable' %} + {% trans 'Ratio' %} {% trans 'Comment' %} {% trans 'Action' %} @@ -25,19 +27,6 @@ -
    -
    - -
    - -
    -
    -
    {% endblock %} {% block custom_foot_js %}