diff --git a/apps/assets/api.py b/apps/assets/api.py index f3bfedb5a..bd22c7336 100644 --- a/apps/assets/api.py +++ b/apps/assets/api.py @@ -17,15 +17,15 @@ from rest_framework import generics 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 rest_framework.pagination import LimitOffsetPagination +from django.shortcuts import get_object_or_404 +from django.db.models import Q, Count from common.mixins import CustomFilterMixin from common.utils import get_logger from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \ get_user_granted_assets -from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser +from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser, Label from . import serializers from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \ test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \ @@ -295,3 +295,17 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView): system_user = self.get_object() test_system_user_connectability_manual.delay(system_user) return Response({"msg": "Task created"}) + + +class LabelViewSet(BulkModelViewSet): + queryset = Label.objects.annotate(asset_count=Count("assets"))\ + .annotate(admin_user_count=Count("adminuser")) \ + .annotate(system_user_count=Count("systemuser")) + permission_classes = (IsSuperUser,) + serializer_class = serializers.LabelSerializer + + def list(self, request, *args, **kwargs): + if request.query_params.get("distinct"): + self.serializer_class = serializers.LabelDistinctSerializer + self.queryset = self.queryset.values("name").distinct() + return super().list(request, *args, **kwargs) diff --git a/apps/assets/models/__init__.py b/apps/assets/models/__init__.py index 38ed90721..6e0621ba8 100644 --- a/apps/assets/models/__init__.py +++ b/apps/assets/models/__init__.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # from .user import AdminUser, SystemUser +from .label import Label from .cluster import * from .group import * from .asset import * diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index c83443077..0b1cdf0ec 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -88,6 +88,8 @@ class Asset(models.Model): 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')) diff --git a/apps/assets/models/label.py b/apps/assets/models/label.py new file mode 100644 index 000000000..d4be2286e --- /dev/null +++ b/apps/assets/models/label.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# + +import uuid +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Label(models.Model): + SYSTEM_CATEGORY = "S" + USER_CATEGORY = "U" + CATEGORY_CHOICES = ( + ("S", _("System")), + ("U", _("User")) + ) + id = models.UUIDField(default=uuid.uuid4, primary_key=True) + name = models.CharField(max_length=128, verbose_name=_("Name")) + value = models.CharField(max_length=128, verbose_name=_("Value")) + category = models.CharField(max_length=128, choices=CATEGORY_CHOICES, verbose_name=_("Category")) + is_active = models.BooleanField(default=True, verbose_name=_("Is active")) + comment = models.TextField(blank=True, null=True, verbose_name=_("Comment")) + date_created = models.DateTimeField( + auto_now_add=True, null=True, blank=True, verbose_name=_('Date created') + ) + + def __str__(self): + return "{}:{}".format(self.name, self.value) + + class Meta: + db_table = "assets_label" diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index aa44a539c..ee71afab1 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -30,6 +30,7 @@ class AssetUser(models.Model): _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ]) _public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key')) + labels = models.ManyToManyField('assets.Label', blank=True, verbose_name=_("Labels")) comment = models.TextField(blank=True, verbose_name=_('Comment')) date_created = models.DateTimeField(auto_now_add=True) date_updated = models.DateTimeField(auto_now=True) diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py index cb939fcc6..7fe14da1b 100644 --- a/apps/assets/serializers.py +++ b/apps/assets/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from rest_framework_bulk.serializers import BulkListSerializer from common.mixins import BulkSerializerMixin -from .models import AssetGroup, Asset, Cluster, AdminUser, SystemUser +from .models import AssetGroup, Asset, Cluster, AdminUser, SystemUser, Label from .const import ADMIN_USER_CONN_CACHE_KEY, SYSTEM_USER_CONN_CACHE_KEY @@ -284,3 +284,44 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer): @staticmethod def get_assets_amount(obj): return len(obj.assets_granted) + + +class LabelSerializer(serializers.ModelSerializer): + asset_count = serializers.SerializerMethodField() + admin_user_count = serializers.SerializerMethodField() + system_user_count = serializers.SerializerMethodField() + + class Meta: + model = Label + fields = '__all__' + list_serializer_class = BulkListSerializer + + @staticmethod + def get_asset_count(obj): + return obj.asset_count + + @staticmethod + def get_admin_user_count(obj): + return obj.admin_user_count + + @staticmethod + def get_system_user_count(obj): + return obj.system_user_count + + def get_field_names(self, declared_fields, info): + fields = super().get_field_names(declared_fields, info) + fields.extend(['get_category_display']) + return fields + + +class LabelDistinctSerializer(serializers.ModelSerializer): + value = serializers.SerializerMethodField() + + class Meta: + model = Label + fields = ("name", "value") + + @staticmethod + def get_value(obj): + labels = Label.objects.filter(name=obj["name"]) + return ', '.join([label.value for label in labels]) diff --git a/apps/assets/templates/assets/label_list.html b/apps/assets/templates/assets/label_list.html new file mode 100644 index 000000000..81c5358b2 --- /dev/null +++ b/apps/assets/templates/assets/label_list.html @@ -0,0 +1,72 @@ +{% extends '_base_list.html' %} +{% load i18n static %} +{% block table_search %}{% endblock %} +{% block table_container %} +
++ + | +{% trans 'Name' %} | +{% trans 'Value' %} | +{% trans 'Asset' %} | +{% trans 'Admin user' %} | +{% trans 'System user' %} | +{% trans 'Action' %} | +
---|