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
b60e5a7e
Commit
b60e5a7e
authored
Mar 23, 2017
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Change] 修改一些view
parent
c940a4c0
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
299 additions
and
24 deletions
+299
-24
asset.py
apps/assets/models/asset.py
+6
-1
user.py
apps/assets/models/user.py
+16
-2
proxy_log_offline_list.html
apps/audits/templates/audits/proxy_log_offline_list.html
+0
-0
proxy_log_online_list.html
apps/audits/templates/audits/proxy_log_online_list.html
+173
-0
views_urls.py
apps/audits/urls/views_urls.py
+7
-5
views.py
apps/audits/views.py
+33
-1
utils.py
apps/common/utils.py
+4
-2
inventory.py
apps/ops/ansible/inventory.py
+6
-6
runner.py
apps/ops/ansible/runner.py
+3
-1
task_detail.html
apps/ops/templates/ops/task_detail.html
+35
-0
utils.py
apps/ops/utils.py
+7
-2
views.py
apps/ops/views.py
+2
-0
tasks.py
apps/perms/tasks.py
+2
-2
_nav.html
apps/templates/_nav.html
+5
-2
.gitkeep
tmp/.gitkeep
+0
-0
No files found.
apps/assets/models/asset.py
View file @
b60e5a7e
...
...
@@ -112,7 +112,12 @@ class Asset(models.Model):
'groups'
:
[
group
.
name
for
group
in
self
.
groups
.
all
()],
'username'
:
self
.
admin_user
.
username
if
self
.
admin_user
else
''
,
'password'
:
self
.
admin_user
.
password
if
self
.
admin_user
else
''
,
'private_key'
:
self
.
admin_user
.
private_key
if
self
.
admin_user
else
None
,
'private_key'
:
self
.
admin_user
.
private_key_file
if
self
.
admin_user
else
None
,
'become'
:
{
'method'
:
self
.
admin_user
.
become_method
,
'user'
:
self
.
admin_user
.
become_user
,
'pass'
:
self
.
admin_user
.
become_pass
,
}
if
self
.
admin_user
.
become
else
{},
}
class
Meta
:
...
...
apps/assets/models/user.py
View file @
b60e5a7e
...
...
@@ -3,12 +3,14 @@
#
from
__future__
import
unicode_literals
import
os
import
logging
from
hashlib
import
md5
from
django.core.exceptions
import
ValidationError
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.conf
import
settings
from
common.utils
import
signer
,
validate_ssh_private_key
,
ssh_key_string_to_obj
...
...
@@ -38,7 +40,7 @@ class AdminUser(models.Model):
become
=
models
.
BooleanField
(
default
=
True
)
become_method
=
models
.
CharField
(
choices
=
BECOME_METHOD_CHOICES
,
default
=
'sudo'
,
max_length
=
4
)
become_user
=
models
.
CharField
(
default
=
'root'
,
max_length
=
64
)
become_pass
word
=
models
.
CharField
(
default
=
''
,
max_length
=
128
)
become_pass
=
models
.
CharField
(
default
=
''
,
max_length
=
128
)
_public_key
=
models
.
CharField
(
max_length
=
4096
,
blank
=
True
,
verbose_name
=
_
(
'SSH public key'
))
comment
=
models
.
TextField
(
blank
=
True
,
verbose_name
=
_
(
'Comment'
))
...
...
@@ -74,6 +76,18 @@ class AdminUser(models.Model):
def
private_key
(
self
,
private_key_raw
):
self
.
_private_key
=
signer
.
sign
(
private_key_raw
)
@property
def
private_key_file
(
self
):
if
not
self
.
private_key
:
return
None
project_dir
=
settings
.
PROJECT_DIR
tmp_dir
=
os
.
path
.
join
(
project_dir
,
'tmp'
)
key_name
=
md5
(
self
.
_private_key
)
.
hexdigest
()
key_path
=
os
.
path
.
join
(
tmp_dir
,
key_name
)
if
not
os
.
path
.
exists
(
key_path
):
self
.
private_key
.
write_private_key_file
(
key_path
)
return
key_path
@property
def
public_key
(
self
):
return
signer
.
unsign
(
self
.
_public_key
)
...
...
apps/audits/templates/audits/proxy_log_list.html
→
apps/audits/templates/audits/proxy_log_
offline_
list.html
View file @
b60e5a7e
File moved
apps/audits/templates/audits/proxy_log_online_list.html
0 → 100644
View file @
b60e5a7e
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% block content_left_head %}
<link
href=
"{% static 'css/plugins/datepicker/datepicker3.css' %}"
rel=
"stylesheet"
>
<style>
#search_btn
{
margin-bottom
:
0
;
}
</style>
{% endblock %}
{% block table_search %}
<form
id=
"search_form"
method=
"get"
action=
""
class=
"pull-right form-inline"
>
<div
class=
"form-group"
id=
"date"
>
<div
class=
"input-daterange input-group"
id=
"datepicker"
>
<span
class=
"input-group-addon"
><i
class=
"fa fa-calendar"
></i></span>
<input
type=
"text"
class=
"input-sm form-control"
style=
"width: 100px;"
name=
"date_from"
value=
"{{ date_from }}"
>
<span
class=
"input-group-addon"
>
to
</span>
<input
type=
"text"
class=
"input-sm form-control"
style=
"width: 100px;"
name=
"date_to"
value=
"{{ date_to }}"
>
</div>
</div>
<div
class=
"input-group"
>
<select
class=
"select2 form-control"
name=
"username"
>
<option
value=
""
>
{% trans 'User' %}
</option>
{% for user in user_list %}
<option
value=
"{{ user }}"
{%
if
user =
=
username
%}
selected
{%
endif
%}
>
{{ user }}
</option>
{% endfor %}
</select>
</div>
<div
class=
"input-group"
>
<select
class=
"select2 form-control"
name=
"ip"
>
<option
value=
""
>
{% trans 'Asset' %}
</option>
{% for asset in asset_list %}
<option
value=
"{{ asset }}"
{%
if
asset =
=
ip
%}
selected
{%
endif
%}
>
{{ asset }}
</option>
{% endfor %}
</select>
</div>
<div
class=
"input-group"
>
<select
class=
"select2 form-control"
name=
"system_user"
>
<option
value=
""
>
{% trans 'System user' %}
</option>
{% for su in system_user_list %}
<option
value=
"{{ su }}"
{%
if
su =
=
system_user
%}
selected
{%
endif
%}
>
{{ su }}
</option>
{% endfor %}
</select>
</div>
<div
class=
"input-group"
>
<input
type=
"text"
class=
"form-control input-sm"
name=
"keyword"
placeholder=
"Keyword"
value=
"{{ keyword }}"
>
</div>
<div
class=
"input-group"
>
<div
class=
"input-group-btn"
>
<button
id=
'search_btn'
type=
"submit"
class=
"btn btn-sm btn-primary"
>
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th
class=
"text-center"
></th>
<th
class=
"text-center"
>
{% trans 'ID' %}
</th>
<th
class=
"text-center"
>
{% trans 'User' %}
</th>
<th
class=
"text-center"
>
{% trans 'Asset' %}
</th>
<th
class=
"text-center"
>
{% trans 'System user' %}
</th>
<th
class=
"text-center"
>
{% trans 'Terminal' %}
</th>
<th
class=
"text-center"
>
{% trans 'Command' %}
</th>
<th
class=
"text-center"
>
{% trans 'Success' %}
</th>
<th
class=
"text-center"
>
{% trans 'Finished' %}
</th>
<th
class=
"text-center"
>
{% trans 'R/M' %}
</th>
<th
class=
"text-center"
>
{% trans 'Date start' %}
</th>
<th
class=
"text-center"
>
{% trans 'Time' %}
</th>
{% endblock %}
{% block table_body %}
{% for proxy_log in proxy_log_list %}
<tr
class=
"gradeX"
>
<td
class=
"text-center"
><input
type=
"checkbox"
class=
"cbx-term"
value=
"{{ proxy_log.id }}"
></td>
<td
class=
"text-center"
>
<a
href=
"{% url 'audits:proxy-log-detail' pk=proxy_log.id %}"
>
{{ proxy_log.id }}
</a>
</td>
<td
class=
"text-center"
>
{{ proxy_log.user }}
</td>
<td
class=
"text-center"
>
{{ proxy_log.asset }}
</td>
<td
class=
"text-center"
>
{{ proxy_log.system_user }}
</td>
<td
class=
"text-center"
>
{{ proxy_log.terminal }}
</td>
<td
class=
"text-center"
>
{{ proxy_log.commands.all|length}}
</td>
<td
class=
"text-center"
>
{% if proxy_log.is_failed %}
<i
class=
"fa fa-times text-danger"
></i>
{% else %}
<i
class=
"fa fa-check text-navy"
></i>
{% endif %}
</td>
{% if proxy_log.is_finished %}
<td
class=
"text-center"
>
<i
class=
"fa fa-check text-navy"
></i>
</td>
<td
class=
"text-center"
>
<a><span
class=
"text-navy"
><i
class=
"fa fa-play-circle"
></i></span></a>
</td>
{% else %}
<td
class=
"text-center"
>
<a
class=
"btn-term"
value=
"{{ proxy_log.id }}"
><i
class=
"fa fa-times text-danger"
></i></a>
</td>
<td
class=
"text-center"
>
<a><span
class=
"text-danger"
><i
class=
"fa fa-eye"
></i></span></a>
</td>
{% endif %}
<td
class=
"text-center"
>
{{ proxy_log.date_start }}
</td>
<td
class=
"text-center"
>
{{ proxy_log.date_finished|timeuntil:proxy_log.date_start }}
</td>
</tr>
{% endfor %}
{% endblock %}
{% block content_bottom_left %}
<div
id=
"actions"
>
<div
class=
"input-group"
>
<select
class=
"form-control m-b"
style=
"width: auto"
id=
"slct_bulk_update"
>
<option
value=
"terminate"
>
{% trans 'Terminate selected' %}
</option>
</select>
<div
class=
"input-group-btn pull-left"
style=
"padding-left: 5px;"
>
<button
id=
'btn_bulk_update'
style=
"height: 32px;"
class=
"btn btn-sm btn-primary"
>
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script>
function
terminateConnection
(
data
)
{
function
success
()
{
window
.
setTimeout
(
function
()
{
window
.
location
.
reload
()
},
300
)
}
var
the_url
=
"{% url 'api-applications:terminate-connection' %}"
;
APIUpdateAttr
({
url
:
the_url
,
method
:
'POST'
,
body
:
JSON
.
stringify
(
data
),
success
:
success
,
success_message
:
'Terminate success'
});
}
$
(
document
).
ready
(
function
()
{
$
(
'table'
).
DataTable
({
"searching"
:
false
,
"paging"
:
false
,
"bInfo"
:
false
,
"order"
:
[]
});
$
(
'.select2'
).
select2
();
$
(
'#date .input-daterange'
).
datepicker
({
dateFormat
:
'mm/dd/yy'
,
keyboardNavigation
:
false
,
forceParse
:
false
,
autoclose
:
true
});
}).
on
(
'click'
,
'.btn-term'
,
function
()
{
var
$this
=
$
(
this
);
var
proxy_log_id
=
$this
.
attr
(
'value'
);
var
data
=
{
proxy_log_id
:
proxy_log_id
};
terminateConnection
(
data
)
}).
on
(
'click'
,
'#btn_bulk_update'
,
function
()
{
var
data
=
[];
$
(
'.cbx-term:checked'
).
each
(
function
()
{
data
.
push
({
proxy_log_id
:
$
(
this
).
attr
(
'value'
)})
});
terminateConnection
(
data
)
})
</script>
{% endblock %}
apps/audits/urls/views_urls.py
View file @
b60e5a7e
...
...
@@ -4,14 +4,16 @@ from .. import views
app_name
=
'audits'
urlpatterns
=
[
url
(
r'^proxy-log$'
,
views
.
ProxyLogListView
.
as_view
(),
name
=
'proxy-log-list'
),
url
(
r'^proxy-log/(?P<pk>\d+)$'
,
views
.
ProxyLogDetailView
.
as_view
(),
url
(
r'^proxy-log-offline/$'
,
views
.
ProxyLogOfflineListView
.
as_view
(),
name
=
'proxy-log-offline-list'
),
url
(
r'^proxy-log-online/$'
,
views
.
ProxyLogOnlineListView
.
as_view
(),
name
=
'proxy-log-online-list'
),
url
(
r'^proxy-log/(?P<pk>\d+)/$'
,
views
.
ProxyLogDetailView
.
as_view
(),
name
=
'proxy-log-detail'
),
# url(r'^proxy-log/(?P<pk>\d+)/commands$', views.ProxyLogCommandsListView.as_view(), name='proxy-log-commands-list'),
url
(
r'^command-log$'
,
views
.
CommandLogListView
.
as_view
(),
url
(
r'^command-log
/
$'
,
views
.
CommandLogListView
.
as_view
(),
name
=
'command-log-list'
),
url
(
r'^login-log$'
,
views
.
LoginLogListView
.
as_view
(),
url
(
r'^login-log
/
$'
,
views
.
LoginLogListView
.
as_view
(),
name
=
'login-log-list'
),
]
...
...
apps/audits/views.py
View file @
b60e5a7e
...
...
@@ -22,7 +22,7 @@ from audits.backends import CommandLogSerializer
class
ProxyLogListView
(
AdminUserRequiredMixin
,
ListView
):
model
=
ProxyLog
template_name
=
'audits/proxy_log_list.html'
template_name
=
'audits/proxy_log_
online_
list.html'
context_object_name
=
'proxy_log_list'
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
keyword
=
user
=
asset
=
system_user
=
date_from_s
=
date_to_s
=
''
...
...
@@ -89,6 +89,38 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return
super
(
ProxyLogListView
,
self
)
.
get_context_data
(
**
kwargs
)
class
ProxyLogOfflineListView
(
ProxyLogListView
):
template_name
=
'audits/proxy_log_online_list.html'
def
get_queryset
(
self
):
queryset
=
super
(
ProxyLogOfflineListView
,
self
)
.
get_queryset
()
queryset
=
queryset
.
filter
(
is_finished
=
True
)
return
queryset
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'action'
:
_
(
'Proxy log offline list'
),
}
kwargs
.
update
(
context
)
return
super
(
ProxyLogOfflineListView
,
self
)
.
get_context_data
(
**
kwargs
)
class
ProxyLogOnlineListView
(
ProxyLogListView
):
template_name
=
'audits/proxy_log_online_list.html'
def
get_queryset
(
self
):
queryset
=
super
(
ProxyLogOnlineListView
,
self
)
.
get_queryset
()
queryset
=
queryset
.
filter
(
is_finished
=
False
)
return
queryset
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'action'
:
_
(
'Proxy log online list'
),
}
kwargs
.
update
(
context
)
return
super
(
ProxyLogOnlineListView
,
self
)
.
get_context_data
(
**
kwargs
)
class
ProxyLogDetailView
(
AdminUserRequiredMixin
,
SingleObjectMixin
,
ListView
):
...
...
apps/common/utils.py
View file @
b60e5a7e
...
...
@@ -2,6 +2,7 @@
#
from
__future__
import
unicode_literals
from
collections
import
OrderedDict
from
six
import
string_types
import
base64
import
os
...
...
@@ -53,6 +54,7 @@ def get_object_or_none(model, **kwargs):
class
Signer
(
object
):
"""用来加密,解密,和基于时间戳的方式验证token"""
def
__init__
(
self
,
secret_key
=
SECRET_KEY
):
self
.
secret_key
=
secret_key
...
...
@@ -330,13 +332,13 @@ def encrypt_password(password):
return
None
from
collections
import
OrderedDict
def
capacity_convert
(
size
,
expect
=
'auto'
,
rate
=
1000
):
"""
:param
cap
: '100MB', '1G'
:param
size
: '100MB', '1G'
:param expect: 'K, M, G, T
:param rate: Default 1000, may be 1024
:return:
"""
rate_mapping
=
(
...
...
apps/ops/ansible/inventory.py
View file @
b60e5a7e
...
...
@@ -21,16 +21,16 @@ class JMSHost(Host):
# 添加密码和秘钥
if
asset
.
get
(
'password'
):
self
.
set_variable
(
'ansible_ssh_pass'
,
asset
[
'password'
])
if
asset
.
get
(
'key'
):
if
asset
.
get
(
'
private_
key'
):
self
.
set_variable
(
'ansible_ssh_private_key_file'
,
asset
[
'private_key'
])
# 添加become支持
become
=
asset
.
get
(
"become"
,
Non
e
)
if
become
is
not
None
:
become
=
asset
.
get
(
"become"
,
Fals
e
)
if
become
:
self
.
set_variable
(
"ansible_become"
,
True
)
self
.
set_variable
(
"ansible_become_method"
,
become
.
get
(
'method'
))
self
.
set_variable
(
"ansible_become_user"
,
become
.
get
(
'user'
))
self
.
set_variable
(
"ansible_become_pass"
,
become
.
get
(
'pass'
))
self
.
set_variable
(
"ansible_become_method"
,
become
.
get
(
'method'
,
'sudo'
))
self
.
set_variable
(
"ansible_become_user"
,
become
.
get
(
'user'
,
'root'
))
self
.
set_variable
(
"ansible_become_pass"
,
become
.
get
(
'pass'
,
''
))
else
:
self
.
set_variable
(
"ansible_become"
,
False
)
...
...
apps/ops/ansible/runner.py
View file @
b60e5a7e
...
...
@@ -265,7 +265,9 @@ class AdHocRunner(object):
result
[
'success'
]
.
append
(
host
)
for
host
,
msgs
in
self
.
results_callback
.
result_q
[
'dark'
]
.
items
():
msg
=
'
\n
'
.
join
([
'{}: {}'
.
format
(
msg
.
get
(
'invocation'
,
{})
.
get
(
'module_name'
),
msg
=
'
\n
'
.
join
([
'{} {}: {}'
.
format
(
msg
.
get
(
'module_stdout'
,
''
),
msg
.
get
(
'invocation'
,
{})
.
get
(
'module_name'
),
msg
.
get
(
'msg'
,
''
))
for
msg
in
msgs
])
result
[
'failed'
]
.
append
((
host
,
msg
))
return
result
...
...
apps/ops/templates/ops/task_detail.html
View file @
b60e5a7e
...
...
@@ -68,7 +68,17 @@
</tr>
<tr>
<td>
{% trans 'Is success ' %}:
</td>
{% if object.is_finished %}
<td><b>
{{ object.is_success|yesno:"Yes,No,Unkown" }}
</b></td>
{% else %}
<td>
<div
class=
"progress progress-striped active"
>
<div
style=
"width: 50%"
aria-valuemax=
"100"
aria-valuemin=
"0"
aria-valuenow=
"75"
role=
"progressbar"
class=
"progress-bar progress-bar-danger"
>
<span
class=
"sr-only"
>
40% Complete (success)
</span>
</div>
</div>
</td>
{% endif %}
</tr>
<tr>
<td>
{% trans 'Assets ' %}:
</td>
...
...
@@ -84,6 +94,31 @@
</table>
</div>
</div>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<span><b>
Result
</b></span>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<ul
class=
"dropdown-menu dropdown-user"
>
</ul>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<pre>
{{ object.result }}
</pre>
</div>
</div>
</div>
</div>
<div
class=
"col-sm-5"
style=
"padding-left: 0;padding-right: 0"
>
<div
class=
"panel panel-danger"
>
...
...
apps/ops/utils.py
View file @
b60e5a7e
...
...
@@ -18,7 +18,7 @@ logger = get_logger(__file__)
def
run_AdHoc
(
task_tuple
,
assets
,
task_name
=
'Ansible AdHoc runner'
,
task_id
=
None
,
pattern
=
'all'
,
record
=
True
,
verbose
=
Fals
e
):
record
=
True
,
verbose
=
Tru
e
):
"""
:param task_tuple: (('module_name', 'module_args'), ('module_name', 'module_args'))
:param assets: [asset1, asset2]
...
...
@@ -51,6 +51,11 @@ def run_AdHoc(task_tuple, assets,
else
:
record
=
Task
.
objects
.
get
(
uuid
=
task_id
)
record
.
date_start
=
timezone
.
now
()
record
.
date_finished
=
None
record
.
timedelta
=
None
record
.
is_finished
=
False
record
.
is_success
=
False
record
.
save
()
ts_start
=
time
.
time
()
if
verbose
:
logger
.
debug
(
'Start runner {}'
.
format
(
task_name
))
...
...
@@ -61,7 +66,7 @@ def run_AdHoc(task_tuple, assets,
record
.
date_finished
=
timezone
.
now
()
record
.
is_finished
=
True
if
verbose
:
record
.
result
=
json
.
dumps
(
result
)
record
.
result
=
json
.
dumps
(
result
,
indent
=
4
,
sort_keys
=
True
)
record
.
summary
=
json
.
dumps
(
summary
)
record
.
timedelta
=
timedelta
if
len
(
summary
[
'failed'
])
==
0
:
...
...
apps/ops/views.py
View file @
b60e5a7e
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
import
time
import
json
from
datetime
import
datetime
...
...
@@ -81,4 +82,5 @@ class TaskRunView(View):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
pk
=
kwargs
.
get
(
self
.
pk_url_kwarg
)
rerun_task
.
delay
(
pk
)
time
.
sleep
(
0.5
)
return
redirect
(
reverse
(
'ops:task-detail'
,
kwargs
=
{
'pk'
:
pk
}))
apps/perms/tasks.py
View file @
b60e5a7e
...
...
@@ -35,10 +35,10 @@ def push_users(self, assets, users):
(
'authorized_key'
,
"user={} state=present key='{}'"
.
format
(
user
[
'username'
],
user
[
'public_key'
])),
(
'lineinfile'
,
"
name=/etc/sudoers state=present regexp='^{0} ALL=(ALL)
' "
"
dest=/etc/sudoers state=present regexp='^{0} ALL=
' "
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
"validate='visudo -cf
%
s'"
.
format
(
user
[
'username'
],
user
.
get
(
'sudo'
,
'/
bin/whoami
'
)
user
[
'username'
],
user
.
get
(
'sudo'
,
'/
sbin/ifconfig
'
)
))
])
task_name
=
'Push user {}'
.
format
(
','
.
join
([
user
[
'name'
]
for
user
in
users
]))
...
...
apps/templates/_nav.html
View file @
b60e5a7e
...
...
@@ -56,8 +56,11 @@
<li
id=
"audits"
>
<a
href=
"#"
><i
class=
"fa fa-files-o"
></i>
<span
class=
"nav-label"
>
{% trans 'Audits' %}
</span><span
class=
"fa arrow"
></span></a>
<ul
class=
"nav nav-second-level"
>
<li
id=
"proxy-log"
>
<a
href=
"{% url 'audits:proxy-log-list' %}"
>
{% trans 'Proxy log' %}
</a>
<li
id=
"proxy-log-offline"
>
<a
href=
"{% url 'audits:proxy-log-offline-list' %}"
>
{% trans 'Session online' %}
</a>
</li>
<li
id=
"proxy-log-online"
>
<a
href=
"{% url 'audits:proxy-log-online-list' %}"
>
{% trans 'Session history' %}
</a>
</li>
<li
id=
"command-log"
>
<a
href=
"{% url 'audits:command-log-list' %}"
>
{% trans 'Command log' %}
</a>
...
...
tmp/.gitkeep
0 → 100644
View file @
b60e5a7e
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