Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
b0fab245
Commit
b0fab245
authored
Jan 26, 2018
by
ibuler
Browse files
Options
Browse Files
Download
Plain Diff
[Merge] 合并标签功能
parents
0a2b6494
b2d6645f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
544 additions
and
60 deletions
+544
-60
api.py
apps/assets/api.py
+16
-3
forms.py
apps/assets/forms.py
+73
-21
__init__.py
apps/assets/models/__init__.py
+1
-0
asset.py
apps/assets/models/asset.py
+2
-0
label.py
apps/assets/models/label.py
+37
-0
serializers.py
apps/assets/serializers.py
+32
-1
asset_create.html
apps/assets/templates/assets/asset_create.html
+45
-8
asset_detail.html
apps/assets/templates/assets/asset_detail.html
+27
-0
asset_list.html
apps/assets/templates/assets/asset_list.html
+22
-0
asset_update.html
apps/assets/templates/assets/asset_update.html
+37
-9
label_create_update.html
apps/assets/templates/assets/label_create_update.html
+32
-0
label_list.html
apps/assets/templates/assets/label_list.html
+69
-0
__init__.py
apps/assets/templatetags/__init__.py
+2
-0
asset_tags.py
apps/assets/templatetags/asset_tags.py
+8
-2
api_urls.py
apps/assets/urls/api_urls.py
+1
-0
views_urls.py
apps/assets/urls/views_urls.py
+3
-0
utils.py
apps/assets/utils.py
+26
-1
__init__.py
apps/assets/views/__init__.py
+1
-0
asset.py
apps/assets/views/asset.py
+9
-7
label.py
apps/assets/views/label.py
+70
-0
common_tags.py
apps/common/templatetags/common_tags.py
+5
-0
django.mo
apps/locale/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/locale/zh/LC_MESSAGES/django.po
+0
-0
jumpserver.css
apps/static/css/jumpserver.css
+3
-2
jumpserver.js
apps/static/js/jumpserver.js
+17
-1
_nav.html
apps/templates/_nav.html
+4
-3
terminal_list.html
apps/terminal/templates/terminal/terminal_list.html
+1
-1
user_detail.html
apps/users/templates/users/user_detail.html
+1
-1
No files found.
apps/assets/api.py
View file @
b0fab245
...
...
@@ -17,25 +17,26 @@ from rest_framework import generics
from
rest_framework.response
import
Response
from
rest_framework_bulk
import
BulkModelViewSet
from
rest_framework_bulk
import
ListBulkCreateUpdateDestroyAPIView
from
rest_framework.pagination
import
LimitOffsetPagination
from
django.shortcuts
import
get_object_or_404
from
django.db.models
import
Q
,
Count
from
rest_framework.pagination
import
LimitOffsetPagination
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
,
\
test_system_user_connectability_manual
from
.utils
import
LabelFilter
logger
=
get_logger
(
__file__
)
class
AssetViewSet
(
CustomFilterMixin
,
BulkModelViewSet
):
class
AssetViewSet
(
CustomFilterMixin
,
LabelFilter
,
BulkModelViewSet
):
"""
API endpoint that allows Asset to be viewed or edited.
"""
...
...
@@ -295,3 +296,15 @@ 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"
))
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
)
apps/assets/forms.py
View file @
b0fab245
...
...
@@ -2,28 +2,35 @@
from
django
import
forms
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
logger
=
get_logger
(
__file__
)
class
AssetCreateForm
(
forms
.
ModelForm
):
class
Meta
:
model
=
Asset
fields
=
[
'hostname'
,
'ip'
,
'public_ip'
,
'port'
,
'type'
,
'comment'
,
'cluster'
,
'groups'
,
'status'
,
'env'
,
'is_active'
,
'admin_user'
'admin_user'
,
'labels'
]
widgets
=
{
'groups'
:
forms
.
SelectMultiple
(
attrs
=
{
'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'
)}),
'port'
:
forms
.
TextInput
()
'groups'
:
forms
.
SelectMultiple
(
attrs
=
{
'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
=
{
'hostname'
:
'* required'
,
...
...
@@ -40,6 +47,14 @@ class AssetCreateForm(forms.ModelForm):
raise
forms
.
ValidationError
(
_
(
"You need set a admin user if cluster not have"
))
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
Meta
:
...
...
@@ -47,11 +62,22 @@ class AssetUpdateForm(forms.ModelForm):
fields
=
[
'hostname'
,
'ip'
,
'port'
,
'groups'
,
"cluster"
,
'is_active'
,
'type'
,
'env'
,
'status'
,
'public_ip'
,
'remote_card_ip'
,
'cabinet_no'
,
'cabinet_pos'
,
'number'
,
'comment'
,
'admin_user'
,
'cabinet_pos'
,
'number'
,
'comment'
,
'admin_user'
,
'labels'
]
widgets
=
{
'groups'
:
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
'data-placeholder'
:
_
(
'Select asset groups'
)}),
'admin_user'
:
forms
.
Select
(
attrs
=
{
'class'
:
'select2'
,
'data-placeholder'
:
_
(
"Default using cluster admin user"
)})
'groups'
:
forms
.
SelectMultiple
(
attrs
=
{
'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
=
{
'hostname'
:
'* required'
,
...
...
@@ -68,13 +94,15 @@ class AssetUpdateForm(forms.ModelForm):
raise
forms
.
ValidationError
(
_
(
"You need set a admin user if cluster not have"
))
return
self
.
cleaned_data
[
'admin_user'
]
def
is_valid
(
self
):
print
(
self
.
data
)
return
super
()
.
is_valid
()
class
AssetBulkUpdateForm
(
forms
.
ModelForm
):
assets
=
forms
.
ModelMultipleChoiceField
(
required
=
True
,
help_text
=
'* required'
,
label
=
_
(
'Select assets'
),
queryset
=
Asset
.
objects
.
all
(),
required
=
True
,
help_text
=
'* required'
,
label
=
_
(
'Select assets'
),
queryset
=
Asset
.
objects
.
all
(),
widget
=
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
...
...
@@ -83,10 +111,7 @@ class AssetBulkUpdateForm(forms.ModelForm):
)
)
port
=
forms
.
IntegerField
(
label
=
_
(
'Port'
),
required
=
False
,
min_value
=
1
,
max_value
=
65535
,
label
=
_
(
'Port'
),
required
=
False
,
min_value
=
1
,
max_value
=
65535
,
)
class
Meta
:
...
...
@@ -96,7 +121,9 @@ class AssetBulkUpdateForm(forms.ModelForm):
'type'
,
'env'
,
]
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
):
...
...
@@ -140,7 +167,7 @@ class AssetGroupForm(forms.ModelForm):
def
save
(
self
,
commit
=
True
):
group
=
super
()
.
save
(
commit
=
commit
)
assets
=
self
.
cleaned_data
[
'assets'
]
assets
=
self
.
cleaned_data
[
'assets'
]
group
.
assets
.
set
(
assets
)
return
group
...
...
@@ -377,3 +404,28 @@ class SystemUserAuthForm(forms.Form):
class
FileForm
(
forms
.
Form
):
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
apps/assets/models/__init__.py
View file @
b0fab245
...
...
@@ -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
*
...
...
apps/assets/models/asset.py
View file @
b0fab245
...
...
@@ -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'
))
...
...
apps/assets/models/label.py
0 → 100644
View file @
b0fab245
# -*- 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'
)
apps/assets/serializers.py
View file @
b0fab245
...
...
@@ -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
...
...
@@ -286,3 +286,34 @@ class MyAssetGroupGrantedSerializer(serializers.ModelSerializer):
@staticmethod
def
get_assets_amount
(
obj
):
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
])
apps/assets/templates/assets/asset_create.html
View file @
b0fab245
...
...
@@ -2,6 +2,8 @@
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block form %}
<form
action=
""
method=
"post"
class=
"form-horizontal"
>
...
...
@@ -28,12 +30,37 @@
<h3>
{% trans 'Group' %}
</h3>
{% 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>
<h3>
{% trans 'Other' %}
</h3>
{% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"form-group"
>
<div
class=
"col-sm-4 col-sm-offset-2"
>
...
...
@@ -45,11 +72,20 @@
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
allowClear
:
true
});
})
</script>
<script>
function
format
(
item
)
{
var
group
=
item
.
element
.
parentElement
.
label
;
return
group
+
':'
+
item
.
text
;
}
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
allowClear
:
true
});
$
(
".labels"
).
select2
({
allowClear
:
true
,
templateSelection
:
format
});
})
</script>
{% endblock %}
\ No newline at end of file
apps/assets/templates/assets/asset_detail.html
View file @
b0fab245
...
...
@@ -244,6 +244,33 @@
</table>
</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 %}
</div>
</div>
...
...
apps/assets/templates/assets/asset_list.html
View file @
b0fab245
...
...
@@ -23,6 +23,14 @@
{% 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=
"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"
>
<thead>
<tr>
...
...
@@ -114,6 +122,20 @@ function initTable() {
$
(
document
).
ready
(
function
(){
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
()
{
var
$data_table
=
$
(
'#asset_list_table'
).
DataTable
();
...
...
apps/assets/templates/assets/asset_update.html
View file @
b0fab245
...
...
@@ -2,6 +2,8 @@
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block custom_head_css_js_create %}
<link
href=
"{% static "
css
/
plugins
/
inputTags
.
css
"
%}"
rel=
"stylesheet"
>
...
...
@@ -33,6 +35,27 @@
<h3>
{% trans 'Group' %}
</h3>
{% 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>
<h3>
{% trans 'Configuration' %}
</h3>
{% bootstrap_field form.number layout="horizontal" %}
...
...
@@ -62,14 +85,18 @@
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
allowClear
:
true
});
$
(
"#tags"
).
select2
({
tags
:
true
,
maximumSelectionLength
:
8
//最多能够选择的个数
});
})
function
format
(
item
)
{
var
group
=
item
.
element
.
parentElement
.
label
;
return
group
+
':'
+
item
.
text
;
}
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
({
allowClear
:
true
});
$
(
".labels"
).
select2
({
allowClear
:
true
,
templateSelection
:
format
});
})
</script>
{% endblock %}
\ No newline at end of file
apps/assets/templates/assets/label_create_update.html
0 → 100644
View file @
b0fab245
{% 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
apps/assets/templates/assets/label_list.html
0 → 100644
View file @
b0fab245
{% 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 %}
apps/assets/templatetags/__init__.py
0 → 100644
View file @
b0fab245
# -*- coding: utf-8 -*-
#
apps/assets/templatetags/asset_tags.py
View file @
b0fab245
from
collections
import
defaultdict
from
django
import
template
from
django.utils
import
timezone
from
django.conf
import
settings
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
()]
apps/assets/urls/api_urls.py
View file @
b0fab245
...
...
@@ -12,6 +12,7 @@ router.register(r'v1/assets', api.AssetViewSet, 'asset')
router
.
register
(
r'v1/clusters'
,
api
.
ClusterViewSet
,
'cluster'
)
router
.
register
(
r'v1/admin-user'
,
api
.
AdminUserViewSet
,
'admin-user'
)
router
.
register
(
r'v1/system-user'
,
api
.
SystemUserViewSet
,
'system-user'
)
router
.
register
(
r'v1/labels'
,
api
.
LabelViewSet
,
'label'
)
urlpatterns
=
[
url
(
r'^v1/assets-bulk/$'
,
api
.
AssetListUpdateApi
.
as_view
(),
name
=
'asset-bulk-update'
),
...
...
apps/assets/urls/views_urls.py
View file @
b0fab245
...
...
@@ -53,5 +53,8 @@ urlpatterns = [
# url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset-group$', views.SystemUserAssetGroupView.as_view(),
# 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'
),
]
apps/assets/utils.py
View file @
b0fab245
# ~*~ coding: utf-8 ~*~
#
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
.models
import
Asset
,
SystemUser
from
.models
import
Asset
,
SystemUser
,
Label
def
get_assets_by_id_list
(
id_list
):
...
...
@@ -27,3 +32,23 @@ def check_assets_have_system_user(assets, system_users):
if
asset
.
cluster
not
in
clusters
:
errors
[
asset
]
.
append
(
system_user
)
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
apps/assets/views/__init__.py
View file @
b0fab245
...
...
@@ -4,4 +4,5 @@ from .group import *
from
.cluster
import
*
from
.system_user
import
*
from
.admin_user
import
*
from
.label
import
*
apps/assets/views/asset.py
View file @
b0fab245
...
...
@@ -27,7 +27,7 @@ from common.mixins import JSONResponseMixin
from
common.utils
import
get_object_or_none
,
get_logger
,
is_uuid
from
common.const
import
create_success_msg
,
update_success_msg
from
..
import
forms
from
..models
import
Asset
,
AssetGroup
,
AdminUser
,
Cluster
,
SystemUser
from
..models
import
Asset
,
AssetGroup
,
AdminUser
,
Cluster
,
SystemUser
,
Label
from
..hands
import
AdminUserRequiredMixin
...
...
@@ -48,6 +48,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
'app'
:
_
(
'Assets'
),
'action'
:
_
(
'Asset list'
),
'system_users'
:
SystemUser
.
objects
.
all
(),
'labels'
:
Label
.
objects
.
all
()
.
order_by
(
'name'
),
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
...
@@ -72,12 +73,13 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
template_name
=
'assets/asset_create.html'
success_url
=
reverse_lazy
(
'assets:asset-list'
)
def
form_valid
(
self
,
form
):
asset
=
form
.
save
()
asset
.
created_by
=
self
.
request
.
user
.
username
or
'Admin'
asset
.
date_created
=
timezone
.
now
()
asset
.
save
()
return
super
()
.
form_valid
(
form
)
# def form_valid(self, form):
# print("form valid")
# asset = form.save()
# asset.created_by = self.request.user.username or 'Admin'
# asset.date_created = timezone.now()
# asset.save()
# return super().form_valid(form)
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
...
...
apps/assets/views/label.py
0 → 100644
View file @
b0fab245
# -*- 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
apps/common/templatetags/common_tags.py
View file @
b0fab245
...
...
@@ -92,3 +92,8 @@ def is_bool_field(field):
return
True
else
:
return
False
@register.filter
def
to_dict
(
data
):
return
dict
(
data
)
apps/locale/zh/LC_MESSAGES/django.mo
View file @
b0fab245
No preview for this file type
apps/locale/zh/LC_MESSAGES/django.po
View file @
b0fab245
This diff is collapsed.
Click to expand it.
apps/static/css/jumpserver.css
View file @
b0fab245
...
...
@@ -338,4 +338,6 @@ div.dataTables_wrapper div.dataTables_filter {
.nav.nav-tabs
li
.active
a
{
border
:
none
;
}
\ No newline at end of file
}
apps/static/js/jumpserver.js
View file @
b0fab245
...
...
@@ -383,7 +383,22 @@ jumpserver.initServerSideDataTable = function (options) {
}
if
(
data
.
search
!==
null
)
{
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
)
{
var
col
=
data
.
order
[
0
].
column
;
...
...
@@ -446,6 +461,7 @@ jumpserver.initServerSideDataTable = function (options) {
}
});
jumpserver
.
table
=
table
;
return
table
;
};
...
...
apps/templates/_nav.html
View file @
b0fab245
...
...
@@ -24,6 +24,7 @@
<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=
"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>
</li>
<li
id=
"perms"
>
...
...
@@ -36,13 +37,13 @@
</li>
<li
id=
"terminal"
>
<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>
<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-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>
</li>
<li
id=
"ops"
>
...
...
apps/terminal/templates/terminal/terminal_list.html
View file @
b0fab245
...
...
@@ -30,7 +30,7 @@
<th
class=
"text-center"
>
{% trans 'Addr' %}
</th>
<th
class=
"text-center"
>
{% trans 'SSH port' %}
</th>
<th
class=
"text-center"
>
{% trans 'Http port' %}
</th>
<th
class=
"text-center"
>
{% trans 'Session
s
' %}
</th>
<th
class=
"text-center"
>
{% trans 'Session' %}
</th>
<th
class=
"text-center"
>
{% trans 'Active' %}
</th>
<th
class=
"text-center"
>
{% trans 'Alive' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
...
...
apps/users/templates/users/user_detail.html
View file @
b0fab245
...
...
@@ -129,7 +129,7 @@
<td><span
class=
"pull-right"
>
<div
class=
"switch"
>
<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"
>
<span
class=
"onoffswitch-inner"
></span>
<span
class=
"onoffswitch-switch"
></span>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment