diff --git a/README.md b/README.md index 14a2c4143..0177c1abe 100644 --- a/README.md +++ b/README.md @@ -26,36 +26,7 @@ Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互 ### Install 安装 - 1. 安装 Python3 - 略 - - 2. 安装依赖 - - ``` - $ cd requirements && yum -y install $(cat rpm_requirements.txt) && pip install -r requirements.txt - ``` - - 3. 修改配置文件 - - ``` - $ cp config_example.py config.py - ``` - - 4. 修改表结构 - - ``` - $ cd apps && python manage.py makemigrations && python manage.py migrate - ``` - - 5. 运行 - - ``` - $ python run_server.py - ``` - - 6. 其它 - - 整合luna,coco需要nginx来配合, 详见详细安装文档 + [详细安装](https://github.com/jumpserver/jumpserver/wiki/v0.5.0-%E5%9F%BA%E4%BA%8E-CentOS7) ### Usage 使用 diff --git a/apps/assets/api.py b/apps/assets/api.py index e5c5a44ff..5f1c73bf0 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -18,9 +18,10 @@ 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 django.db.models import Q, Count +from rest_framework.pagination import LimitOffsetPagination -from common.mixins import IDInFilterMixin +from common.mixins import CustomFilterMixin from common.utils import get_logger from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \ get_user_granted_assets @@ -34,12 +35,16 @@ from .tasks import update_asset_hardware_info_manual, test_admin_user_connectabi logger = get_logger(__file__) -class AssetViewSet(IDInFilterMixin, BulkModelViewSet): +class AssetViewSet(CustomFilterMixin, BulkModelViewSet): """ API endpoint that allows Asset to be viewed or edited. """ + filter_fields = ("hostname", "ip") + search_fields = filter_fields + ordering_fields = ("hostname", "ip", "port", "cluster", "type", "env", "cpu_cores") queryset = Asset.objects.all() serializer_class = serializers.AssetSerializer + pagination_class = LimitOffsetPagination permission_classes = (IsSuperUserOrAppUser,) def get_queryset(self): @@ -78,11 +83,11 @@ class UserAssetListView(generics.ListAPIView): return queryset -class AssetGroupViewSet(IDInFilterMixin, BulkModelViewSet): +class AssetGroupViewSet(CustomFilterMixin, BulkModelViewSet): """ Asset group api set, for add,delete,update,list,retrieve resource """ - queryset = AssetGroup.objects.all() + queryset = AssetGroup.objects.all().annotate(asset_count=Count("assets")) serializer_class = serializers.AssetGroupSerializer permission_classes = (IsSuperUser,) @@ -112,7 +117,7 @@ class GroupAddAssetsApi(generics.UpdateAPIView): return Response({'error': serializer.errors}, status=400) -class ClusterViewSet(IDInFilterMixin, BulkModelViewSet): +class ClusterViewSet(CustomFilterMixin, BulkModelViewSet): """ Cluster api set, for add,delete,update,list,retrieve resource """ @@ -153,7 +158,7 @@ class ClusterAddAssetsApi(generics.UpdateAPIView): return Response({'error': serializer.errors}, status=400) -class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet): +class AdminUserViewSet(CustomFilterMixin, BulkModelViewSet): """ Admin user api set, for add,delete,update,list,retrieve resource """ @@ -189,7 +194,7 @@ class SystemUserViewSet(BulkModelViewSet): permission_classes = (IsSuperUserOrAppUser,) -class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): +class AssetListUpdateApi(CustomFilterMixin, ListBulkCreateUpdateDestroyAPIView): """ Asset bulk update api """ diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 72d38f85f..824ef3fcd 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -93,7 +93,7 @@ class AssetBulkUpdateForm(forms.ModelForm): model = Asset fields = [ 'assets', 'port', 'groups', "cluster", - 'type', 'env', 'status', + 'type', 'env', ] widgets = { 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), @@ -124,20 +124,25 @@ class AssetGroupForm(forms.ModelForm): label=_('Asset'), required=False, widget=forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('Select assets')}) + attrs={'class': 'select2', 'data-placeholder': _('Select assets')} ) + ) - def __init__(self, *args, **kwargs): - if kwargs.get('instance', None): + def __init__(self, **kwargs): + instance = kwargs.get('instance') + if instance: initial = kwargs.get('initial', {}) - initial['assets'] = kwargs['instance'].assets.all() - super(AssetGroupForm, self).__init__(*args, **kwargs) + initial.update({ + 'assets': instance.assets.all(), + }) + kwargs['initial'] = initial + super().__init__(**kwargs) - def _save_m2m(self): - super(AssetGroupForm, self)._save_m2m() - assets = self.cleaned_data['assets'] - self.instance.assets.clear() - self.instance.assets.add(*tuple(assets)) + def save(self, commit=True): + group = super().save(commit=commit) + assets= self.cleaned_data['assets'] + group.assets.set(assets) + return group class Meta: model = AssetGroup @@ -253,9 +258,10 @@ class SystemUserForm(forms.ModelForm): # Admin user assets define, let user select, save it in form not in view auto_generate_key = forms.BooleanField(initial=True, required=False) # Form field name can not start with `_`, so redefine it, - password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=128, strip=True) + password = forms.CharField(widget=forms.PasswordInput, required=False, + max_length=128, strip=True, label=_("Password")) # Need use upload private key file except paste private key content - private_key_file = forms.FileField(required=False) + private_key_file = forms.FileField(required=False, label=_("Private key")) def save(self, commit=True): # Because we define custom field, so we need rewrite :method: `save` @@ -302,8 +308,11 @@ class SystemUserForm(forms.ModelForm): 'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'username': forms.TextInput(attrs={'placeholder': _('Username')}), 'cluster': forms.SelectMultiple( - attrs={'class': 'select2', - 'data-placeholder': _(' Select clusters')}), + attrs={ + 'class': 'select2', + 'data-placeholder': _(' Select clusters') + } + ), } help_texts = { 'name': '* required', diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 276b767d3..c83443077 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -18,6 +18,16 @@ __all__ = ['Asset'] logger = logging.getLogger(__name__) +def default_cluster(): + from .cluster import Cluster + name = "Default" + defaults = {"name": name} + cluster, created = Cluster.objects.get_or_create( + defaults=defaults, name=name + ) + return cluster.id + + class Asset(models.Model): # Todo: Move them to settings STATUS_CHOICES = ( @@ -44,7 +54,7 @@ class Asset(models.Model): hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) port = models.IntegerField(default=22, verbose_name=_('Port')) groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) - cluster = models.ForeignKey(Cluster, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('Cluster')) + cluster = models.ForeignKey(Cluster, related_name='assets', default=default_cluster, on_delete=models.SET_DEFAULT, verbose_name=_('Cluster')) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),) env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),) diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index 401aeac2a..024d88e22 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -22,7 +22,7 @@ class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer): @staticmethod def get_assets_amount(obj): - return obj.assets.count() + return obj.asset_count class AssetUpdateSystemUserSerializer(serializers.ModelSerializer): @@ -191,9 +191,11 @@ class AssetGrantedSerializer(serializers.ModelSerializer): class Meta(object): model = Asset - fields = ("id", "hostname", "ip", "port", "system_users_granted", - "is_inherited", "is_active", "system_users_join", "os", - "platform", "comment",) + fields = ( + "id", "hostname", "ip", "port", "system_users_granted", + "is_inherited", "is_active", "system_users_join", "os", + "platform", "comment" + ) @staticmethod def get_is_inherited(obj): @@ -214,8 +216,11 @@ class MyAssetGrantedSerializer(AssetGrantedSerializer): class Meta(object): model = Asset - fields = ("id", "hostname", "system_users_granted", "is_inherited", - "is_active", "system_users_join", "comment") + fields = ( + "id", "hostname", "system_users_granted", + "is_inherited", "is_active", "system_users_join", + "os", "platform", "comment", + ) class ClusterSerializer(BulkSerializerMixin, serializers.ModelSerializer): diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index b21c0377f..171cfa6d9 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- # - -from django.db.models.signals import post_save, post_init, m2m_changed, pre_save +from django.db.models.signals import post_save, post_init from django.dispatch import receiver from django.utils.translation import gettext as _ @@ -27,16 +26,17 @@ def test_asset_conn_on_created(asset): def push_cluster_system_users_to_asset(asset): - logger.info("Push cluster system user to asset: {}".format(asset)) - task_name = _("Push cluster system users to asset") - system_users = asset.cluster.systemuser_set.all() - push_system_user_util.delay(system_users, [asset], task_name) + if asset.cluster: + logger.info("Push cluster system user to asset: {}".format(asset)) + task_name = _("Push cluster system users to asset") + system_users = asset.cluster.systemuser_set.all() + push_system_user_util.delay(system_users, [asset], task_name) @receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier") def on_asset_created(sender, instance=None, created=False, **kwargs): if instance and created: - logger.info("Asset `` create signal received".format(instance)) + logger.info("Asset `{}` create signal received".format(instance)) update_asset_hardware_info_on_created(instance) test_asset_conn_on_created(instance) push_cluster_system_users_to_asset(instance) diff --git a/apps/assets/templates/assets/admin_user_assets.html b/apps/assets/templates/assets/admin_user_assets.html index dcaaef312..7ec123fe2 100644 --- a/apps/assets/templates/assets/admin_user_assets.html +++ b/apps/assets/templates/assets/admin_user_assets.html @@ -34,7 +34,7 @@
{% trans 'Hardware' %} | {% trans 'Active' %} | {% trans 'Connective' %} | -{% trans 'Action' %} |
---|