Commit b0fab245 authored by ibuler's avatar ibuler

[Merge] 合并标签功能

parents 0a2b6494 b2d6645f
...@@ -17,25 +17,26 @@ from rest_framework import generics ...@@ -17,25 +17,26 @@ from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
from rest_framework.pagination import LimitOffsetPagination
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.db.models import Q, Count from django.db.models import Q, Count
from rest_framework.pagination import LimitOffsetPagination
from common.mixins import CustomFilterMixin from common.mixins import CustomFilterMixin
from common.utils import get_logger from common.utils import get_logger
from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \ from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \
get_user_granted_assets 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 . import serializers
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \ from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \
test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \ test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \
test_system_user_connectability_manual test_system_user_connectability_manual
from .utils import LabelFilter
logger = get_logger(__file__) logger = get_logger(__file__)
class AssetViewSet(CustomFilterMixin, BulkModelViewSet): class AssetViewSet(CustomFilterMixin, LabelFilter, BulkModelViewSet):
""" """
API endpoint that allows Asset to be viewed or edited. API endpoint that allows Asset to be viewed or edited.
""" """
...@@ -295,3 +296,15 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView): ...@@ -295,3 +296,15 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
system_user = self.get_object() system_user = self.get_object()
test_system_user_connectability_manual.delay(system_user) test_system_user_connectability_manual.delay(system_user)
return Response({"msg": "Task created"}) return Response({"msg": "Task created"})
class LabelViewSet(BulkModelViewSet):
queryset = Label.objects.annotate(asset_count=Count("assets"))
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)
...@@ -2,28 +2,35 @@ ...@@ -2,28 +2,35 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser, Label
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger
logger = get_logger(__file__) logger = get_logger(__file__)
class AssetCreateForm(forms.ModelForm): class AssetCreateForm(forms.ModelForm):
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', 'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
'cluster', 'groups', 'status', 'env', 'is_active', 'cluster', 'groups', 'status', 'env', 'is_active',
'admin_user' 'admin_user', 'labels'
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), 'groups': forms.SelectMultiple(attrs={
'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}), 'class': 'select2', 'data-placeholder': _('Select asset groups')
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}), }),
'port': forms.TextInput() 'cluster': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select cluster')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Select labels')
}),
'port': forms.TextInput(),
} }
help_texts = { help_texts = {
'hostname': '* required', 'hostname': '* required',
...@@ -40,6 +47,14 @@ class AssetCreateForm(forms.ModelForm): ...@@ -40,6 +47,14 @@ class AssetCreateForm(forms.ModelForm):
raise forms.ValidationError(_("You need set a admin user if cluster not have")) raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user'] return self.cleaned_data['admin_user']
def is_valid(self):
print(self.data)
result = super().is_valid()
if not result:
print(self.errors)
print(self.cleaned_data)
return result
class AssetUpdateForm(forms.ModelForm): class AssetUpdateForm(forms.ModelForm):
class Meta: class Meta:
...@@ -47,11 +62,22 @@ class AssetUpdateForm(forms.ModelForm): ...@@ -47,11 +62,22 @@ class AssetUpdateForm(forms.ModelForm):
fields = [ fields = [
'hostname', 'ip', 'port', 'groups', "cluster", 'is_active', 'hostname', 'ip', 'port', 'groups', "cluster", 'is_active',
'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no', 'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no',
'cabinet_pos', 'number', 'comment', 'admin_user', 'cabinet_pos', 'number', 'comment', 'admin_user', 'labels'
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), 'groups': forms.SelectMultiple(attrs={
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _("Default using cluster admin user")}) 'class': 'select2', 'data-placeholder': _('Select asset groups')
}),
'cluster': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select cluster')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Select labels')
}),
'port': forms.TextInput(),
} }
help_texts = { help_texts = {
'hostname': '* required', 'hostname': '* required',
...@@ -68,13 +94,15 @@ class AssetUpdateForm(forms.ModelForm): ...@@ -68,13 +94,15 @@ class AssetUpdateForm(forms.ModelForm):
raise forms.ValidationError(_("You need set a admin user if cluster not have")) raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user'] return self.cleaned_data['admin_user']
def is_valid(self):
print(self.data)
return super().is_valid()
class AssetBulkUpdateForm(forms.ModelForm): class AssetBulkUpdateForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField( assets = forms.ModelMultipleChoiceField(
required=True, required=True, help_text='* required',
help_text='* required', label=_('Select assets'), queryset=Asset.objects.all(),
label=_('Select assets'),
queryset=Asset.objects.all(),
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={ attrs={
'class': 'select2', 'class': 'select2',
...@@ -83,10 +111,7 @@ class AssetBulkUpdateForm(forms.ModelForm): ...@@ -83,10 +111,7 @@ class AssetBulkUpdateForm(forms.ModelForm):
) )
) )
port = forms.IntegerField( port = forms.IntegerField(
label=_('Port'), label=_('Port'), required=False, min_value=1, max_value=65535,
required=False,
min_value=1,
max_value=65535,
) )
class Meta: class Meta:
...@@ -96,7 +121,9 @@ class AssetBulkUpdateForm(forms.ModelForm): ...@@ -96,7 +121,9 @@ class AssetBulkUpdateForm(forms.ModelForm):
'type', 'env', 'type', 'env',
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), 'groups': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}
),
} }
def save(self, commit=True): def save(self, commit=True):
...@@ -140,7 +167,7 @@ class AssetGroupForm(forms.ModelForm): ...@@ -140,7 +167,7 @@ class AssetGroupForm(forms.ModelForm):
def save(self, commit=True): def save(self, commit=True):
group = super().save(commit=commit) group = super().save(commit=commit)
assets= self.cleaned_data['assets'] assets = self.cleaned_data['assets']
group.assets.set(assets) group.assets.set(assets)
return group return group
...@@ -377,3 +404,28 @@ class SystemUserAuthForm(forms.Form): ...@@ -377,3 +404,28 @@ class SystemUserAuthForm(forms.Form):
class FileForm(forms.Form): class FileForm(forms.Form):
file = forms.FileField() file = forms.FileField()
class LabelForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(), label=_('Asset'), required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
class Meta:
model = Label
fields = ['name', 'value', 'assets']
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super().__init__(*args, **kwargs)
def save(self, commit=True):
label = super().save(commit=commit)
assets = self.cleaned_data['assets']
label.assets.set(assets)
return label
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from .user import AdminUser, SystemUser from .user import AdminUser, SystemUser
from .label import Label
from .cluster import * from .cluster import *
from .group import * from .group import *
from .asset import * from .asset import *
......
...@@ -88,6 +88,8 @@ class Asset(models.Model): ...@@ -88,6 +88,8 @@ class Asset(models.Model):
os_arch = models.CharField(max_length=16, blank=True, null=True, verbose_name=_('OS arch')) 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')) 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')) 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')) 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'))
......
# -*- 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, default=USER_CATEGORY, 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')
)
@classmethod
def get_queryset_group_by_name(cls):
names = cls.objects.values_list('name', flat=True)
for name in names:
yield name, cls.objects.filter(name=name)
def __str__(self):
return "{}:{}".format(self.name, self.value)
class Meta:
db_table = "assets_label"
unique_together = ('name', 'value')
...@@ -4,7 +4,7 @@ from rest_framework import serializers ...@@ -4,7 +4,7 @@ from rest_framework import serializers
from rest_framework_bulk.serializers import BulkListSerializer from rest_framework_bulk.serializers import BulkListSerializer
from common.mixins import BulkSerializerMixin 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 from .const import ADMIN_USER_CONN_CACHE_KEY, SYSTEM_USER_CONN_CACHE_KEY
...@@ -286,3 +286,34 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer): ...@@ -286,3 +286,34 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_assets_amount(obj): def get_assets_amount(obj):
return len(obj.assets_granted) return len(obj.assets_granted)
class LabelSerializer(serializers.ModelSerializer):
asset_count = serializers.SerializerMethodField()
class Meta:
model = Label
fields = '__all__'
list_serializer_class = BulkListSerializer
@staticmethod
def get_asset_count(obj):
return obj.asset_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])
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block form %} {% block form %}
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">
...@@ -28,12 +30,37 @@ ...@@ -28,12 +30,37 @@
<h3>{% trans 'Group' %}</h3> <h3>{% trans 'Group' %}</h3>
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Labels' %}</h3>
<div class="form-group {% if form.errors.labels %} has-error {% endif %}">
<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 'Select labels' %}" 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>
{% if form.errors.labels %}
{% for e in form.errors.labels %}
<div class="help-block">{{ e }}</div>
{% endfor %}
{% endif %}
</div>
</div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.comment layout="horizontal" %} {% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %} {% bootstrap_field form.is_active layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
...@@ -45,11 +72,20 @@ ...@@ -45,11 +72,20 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { function format(item) {
$('.select2').select2({ var group = item.element.parentElement.label;
allowClear: true return group + ':' + item.text;
}); }
})
</script> $(document).ready(function () {
$('.select2').select2({
allowClear: true
});
$(".labels").select2({
allowClear: true,
templateSelection: format
});
})
</script>
{% endblock %} {% endblock %}
\ No newline at end of file
...@@ -244,6 +244,33 @@ ...@@ -244,6 +244,33 @@
</table> </table>
</div> </div>
</div> </div>
<div class="panel panel-warning">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'Labels' %}
</div>
<div class="panel-body">
{# <table class="table">#}
{# <tbody>#}
{# {% for label in asset.labels.all %}#}
{# <tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %}>#}
{# <td>{{ label.name }}</td>#}
{# <td>#}
{# <span class="pull-right">#}
{# {{ label.value }}#}
{# </span>#}
{# </td>#}
{# </tr>#}
{# {% endfor %}#}
{# </tbody>#}
{# </table>#}
<ul class="tag-list" style="padding: 0">
{% for label in asset.labels.all %}
<li ><a href=""><i class="fa fa-tag"></i> {{ label.name }}:{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
......
...@@ -23,6 +23,14 @@ ...@@ -23,6 +23,14 @@
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset" %} </a></div> <div class="uc pull-left m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset" %} </a></div>
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
{% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="asset_list_table" > <table class="table table-striped table-bordered table-hover " id="asset_list_table" >
<thead> <thead>
<tr> <tr>
...@@ -114,6 +122,20 @@ function initTable() { ...@@ -114,6 +122,20 @@ function initTable() {
$(document).ready(function(){ $(document).ready(function(){
initTable(); initTable();
$(".select2").select2();
})
.on('click', '.labels li', function () {
var val = $(this).text();
{#var origin_val = $("#asset_list_table_filter input").val();#}
{#var new_val;#}
{#if (origin_val === "") {#}
{# new_val = val;#}
{# } else { #}
{# new_val = origin_val + " " + val;#}
{# } #}
$("#asset_list_table_filter input").val(val);
{#$('#asset_list_table').DataTable().search(val).draw();#}
jumpserver.table.search(val).draw();
}) })
.on('click', '.btn_export', function () { .on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable(); var $data_table = $('#asset_list_table').DataTable();
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block custom_head_css_js_create %} {% block custom_head_css_js_create %}
<link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet">
...@@ -33,6 +35,27 @@ ...@@ -33,6 +35,27 @@
<h3>{% trans 'Group' %}</h3> <h3>{% trans 'Group' %}</h3>
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups 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="Select labels" 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>
<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" %}
...@@ -62,14 +85,18 @@ ...@@ -62,14 +85,18 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { function format(item) {
$('.select2').select2({ var group = item.element.parentElement.label;
allowClear: true return group + ':' + item.text;
}); }
$("#tags").select2({ $(document).ready(function () {
tags: true, $('.select2').select2({
maximumSelectionLength: 8 //最多能够选择的个数 allowClear: true
}); });
}) $(".labels").select2({
allowClear: true,
templateSelection: format
});
})
</script> </script>
{% endblock %} {% endblock %}
\ No newline at end of file
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form id="groupForm" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.value layout="horizontal" %}
{% bootstrap_field form.assets 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-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: false
});
});
</script>
{% endblock %}
\ No newline at end of file
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url 'assets:label-create' %}" class="btn btn-sm btn-primary"> {% trans "Create label" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="label_list_table" >
<thead>
<tr>
<th class="text-center">
<input type="checkbox" id="check_all" class="ipt_check_all" >
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Value' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
function initTable() {
var options = {
ele: $('#label_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
{# var detail_btn = '<a href="{% url "assets:label-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';#}
var detail_btn = '<a>' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:label-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_cluster_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:label-list" %}?sort=name',
columns: [
{data: "id"}, {data: "name" }, {data: "value" },
{data: "asset_count" }, {data: "id"}
],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
}
$(document).ready(function(){
initTable();
})
.on('click', '.btn_cluster_delete', function () {
var $this = $(this);
var $data_table = $('#cluster_list_table').DataTable();
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-assets:cluster-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
});
</script>
{% endblock %}
from collections import defaultdict
from django import template from django import template
from django.utils import timezone
from django.conf import settings
register = template.Library() register = template.Library()
@register.filter
def group_labels(queryset):
grouped = defaultdict(list)
for label in queryset:
grouped[label.name].append(label)
return [(name, labels) for name, labels in grouped.items()]
...@@ -12,6 +12,7 @@ router.register(r'v1/assets', api.AssetViewSet, 'asset') ...@@ -12,6 +12,7 @@ router.register(r'v1/assets', api.AssetViewSet, 'asset')
router.register(r'v1/clusters', api.ClusterViewSet, 'cluster') router.register(r'v1/clusters', api.ClusterViewSet, 'cluster')
router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user') router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user')
router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user') router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
router.register(r'v1/labels', api.LabelViewSet, 'label')
urlpatterns = [ urlpatterns = [
url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'), url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
......
...@@ -53,5 +53,8 @@ urlpatterns = [ ...@@ -53,5 +53,8 @@ urlpatterns = [
# url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset-group$', views.SystemUserAssetGroupView.as_view(), # url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset-group$', views.SystemUserAssetGroupView.as_view(),
# name='system-user-asset-group'), # name='system-user-asset-group'),
url(r'^label/$', views.LabelListView.as_view(), name='label-list'),
url(r'^label/create/$', views.LabelCreateView.as_view(), name='label-create'),
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.LabelUpdateView.as_view(), name='label-update'),
] ]
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# #
from collections import defaultdict from collections import defaultdict
from functools import reduce
import operator
from django.db.models import Q
from common.utils import get_object_or_none from common.utils import get_object_or_none
from .models import Asset, SystemUser from .models import Asset, SystemUser, Label
def get_assets_by_id_list(id_list): def get_assets_by_id_list(id_list):
...@@ -27,3 +32,23 @@ def check_assets_have_system_user(assets, system_users): ...@@ -27,3 +32,23 @@ def check_assets_have_system_user(assets, system_users):
if asset.cluster not in clusters: if asset.cluster not in clusters:
errors[asset].append(system_user) errors[asset].append(system_user)
return errors return errors
class LabelFilter:
def filter_queryset(self, queryset):
query_keys = self.request.query_params.keys()
all_label_keys = Label.objects.values_list('name', flat=True)
valid_keys = set(all_label_keys) & set(query_keys)
labels_query = {}
for key in valid_keys:
labels_query[key] = self.request.query_params.get(key)
conditions = []
for k, v in labels_query.items():
query = {'labels__name': k, 'labels__value': v}
conditions.append(query)
if conditions:
for kwargs in conditions:
queryset = queryset.filter(**kwargs)
return queryset
...@@ -4,4 +4,5 @@ from .group import * ...@@ -4,4 +4,5 @@ from .group import *
from .cluster import * from .cluster import *
from .system_user import * from .system_user import *
from .admin_user import * from .admin_user import *
from .label import *
...@@ -27,7 +27,7 @@ from common.mixins import JSONResponseMixin ...@@ -27,7 +27,7 @@ from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger, is_uuid from common.utils import get_object_or_none, get_logger, is_uuid
from common.const import create_success_msg, update_success_msg from common.const import create_success_msg, update_success_msg
from .. import forms from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser, Label
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
...@@ -48,6 +48,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView): ...@@ -48,6 +48,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
'app': _('Assets'), 'app': _('Assets'),
'action': _('Asset list'), 'action': _('Asset list'),
'system_users': SystemUser.objects.all(), 'system_users': SystemUser.objects.all(),
'labels': Label.objects.all().order_by('name'),
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -72,12 +73,13 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): ...@@ -72,12 +73,13 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
template_name = 'assets/asset_create.html' template_name = 'assets/asset_create.html'
success_url = reverse_lazy('assets:asset-list') success_url = reverse_lazy('assets:asset-list')
def form_valid(self, form): # def form_valid(self, form):
asset = form.save() # print("form valid")
asset.created_by = self.request.user.username or 'Admin' # asset = form.save()
asset.date_created = timezone.now() # asset.created_by = self.request.user.username or 'Admin'
asset.save() # asset.date_created = timezone.now()
return super().form_valid(form) # asset.save()
# return super().form_valid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
......
# -*- coding: utf-8 -*-
#
from django.views.generic import TemplateView, CreateView, \
UpdateView, DeleteView, DetailView
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy
from common.mixins import AdminUserRequiredMixin
from common.const import create_success_msg, update_success_msg
from ..models import Label
from ..forms import LabelForm
__all__ = (
"LabelListView", "LabelCreateView", "LabelUpdateView",
"LabelDetailView", "LabelDeleteView",
)
class LabelListView(AdminUserRequiredMixin, TemplateView):
template_name = 'assets/label_list.html'
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Label list'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class LabelCreateView(AdminUserRequiredMixin, CreateView):
model = Label
template_name = 'assets/label_create_update.html'
form_class = LabelForm
success_url = reverse_lazy('assets:label-list')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create label'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class LabelUpdateView(AdminUserRequiredMixin, UpdateView):
model = Label
template_name = 'assets/label_create_update.html'
form_class = LabelForm
success_url = reverse_lazy('assets:label-list')
success_message = update_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Update label'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class LabelDetailView(AdminUserRequiredMixin, DetailView):
pass
class LabelDeleteView(AdminUserRequiredMixin, DeleteView):
pass
...@@ -92,3 +92,8 @@ def is_bool_field(field): ...@@ -92,3 +92,8 @@ def is_bool_field(field):
return True return True
else: else:
return False return False
@register.filter
def to_dict(data):
return dict(data)
This diff is collapsed.
...@@ -338,4 +338,6 @@ div.dataTables_wrapper div.dataTables_filter { ...@@ -338,4 +338,6 @@ div.dataTables_wrapper div.dataTables_filter {
.nav.nav-tabs li.active a { .nav.nav-tabs li.active a {
border: none; border: none;
} }
\ No newline at end of file
...@@ -383,7 +383,22 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -383,7 +383,22 @@ jumpserver.initServerSideDataTable = function (options) {
} }
if (data.search !== null) { if (data.search !== null) {
var search_val = data.search.value; var search_val = data.search.value;
data.search = search_val; var search_list = search_val.split(" ");
var search_attr = {};
var search_raw = [];
search_list.map(function (val, index) {
var kv = val.split(":");
if (kv.length === 2) {
search_attr[kv[0]] = kv[1]
} else {
search_raw.push(kv)
}
});
data.search = search_raw.join("");
$.each(search_attr, function (k, v) {
data[k] = v
})
} }
if (data.order !== null && data.order.length === 1) { if (data.order !== null && data.order.length === 1) {
var col = data.order[0].column; var col = data.order[0].column;
...@@ -446,6 +461,7 @@ jumpserver.initServerSideDataTable = function (options) { ...@@ -446,6 +461,7 @@ jumpserver.initServerSideDataTable = function (options) {
} }
}); });
jumpserver.table = table;
return table; return table;
}; };
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
<li id="cluster"><a href="{% url 'assets:cluster-list' %}">{% trans 'Cluster' %}</a></li> <li id="cluster"><a href="{% url 'assets:cluster-list' %}">{% trans 'Cluster' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li> <li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li> <li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
</ul> </ul>
</li> </li>
<li id="perms"> <li id="perms">
...@@ -36,13 +37,13 @@ ...@@ -36,13 +37,13 @@
</li> </li>
<li id="terminal"> <li id="terminal">
<a> <a>
<i class="fa fa-rocket"></i> <span class="nav-label">{% trans 'Terminal' %}</span><span class="fa arrow"></span> <i class="fa fa-rocket"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span>
</a> </a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li> <li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li> <li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Command' %}</a></li> <li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
</ul> </ul>
</li> </li>
<li id="ops"> <li id="ops">
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
<th class="text-center">{% trans 'Addr' %}</th> <th class="text-center">{% trans 'Addr' %}</th>
<th class="text-center">{% trans 'SSH port' %}</th> <th class="text-center">{% trans 'SSH port' %}</th>
<th class="text-center">{% trans 'Http port' %}</th> <th class="text-center">{% trans 'Http port' %}</th>
<th class="text-center">{% trans 'Sessions' %}</th> <th class="text-center">{% trans 'Session' %}</th>
<th class="text-center">{% trans 'Active' %}</th> <th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Alive' %}</th> <th class="text-center">{% trans 'Alive' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
......
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
<td><span class="pull-right"> <td><span class="pull-right">
<div class="switch"> <div class="switch">
<div class="onoffswitch"> <div class="onoffswitch">
<input type="checkbox" {% if user_object.is_active %} checked {% endif %} {% if request.user == user_object %} disabled="disabled" {% endif %} class="onoffswitch-checkbox disabled" id="is_active"> <input type="checkbox" {% if user_object.is_active %} checked {% endif %} {% if request.user == user_object %} disabled {% endif %} class="onoffswitch-checkbox" id="is_active">
<label class="onoffswitch-label" for="is_active"> <label class="onoffswitch-label" for="is_active">
<span class="onoffswitch-inner"></span> <span class="onoffswitch-inner"></span>
<span class="onoffswitch-switch"></span> <span class="onoffswitch-switch"></span>
......
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