Commit 78700cd2 authored by ibuler's avatar ibuler

merge with version 0.4.0

parents f1d4cae2 695dc96c
*.py[cod]
.idea
test.py
.DS_Store
db.sqlite3
# C extensions
*.so
# Packages
*.egg
*.egg-info
*.pyc
*.pyo
*.swp
.env
env
env*
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
*.egg
*.egg-info
_mailinglist
dump.rdb
.tox
nosetests.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
.settings
.cache/
.idea/
db.sqlite3
config.py
migrations/
*.log
logs/*
keys/*
jumpserver.conf
nohup.out
tmp/*
host_rsa_key
*.bat
tags
FROM jumpserver/base-env-alpine:latest
MAINTAINER Jumpserver Team <ibuler@qq.com>
#RUN apk add --update python gcc python-dev py-pip musl-dev linux-headers \
# libffi-dev openssl-dev jpeg-dev redis && rm -rf /var/cache/apk/*
COPY . /opt/jumpserver
WORKDIR /opt/jumpserver
RUN cp config_example.py config.py
#RUN pip install -r requirements.txt -i https://pypi.doubanio.com/simple
RUN rm -f db.sqlite3 && cd utils && sh make_migrations.sh && sh init_db.sh
EXPOSE 8080
CMD redis-server utils/redis.conf && python run_server.py
FROM alpine:3.4
MAINTAINER Jumpserver Team <ibuler@qq.com>
RUN apk add --update python gcc python-dev py-pip musl-dev linux-headers \
libffi-dev openssl-dev jpeg-dev freetype-dev redis && rm -rf /var/cache/apk/*
COPY ./requirements.txt /tmp
WORKDIR /tmp
RUN pip install -r requirements.txt -i https://pypi.doubanio.com/simple
\ No newline at end of file
// Jumpserver //
~ Jumpserver是什么?
Jumpserver是一款开源的跳板机(堡垒机)产品, 主要使用Python,Django开发
他实现了跳板机(堡垒机)的主要功能,删减、优化了传统堡垒机,致力于为互联网
运维提供服务
~ 版本依赖
* Python 2.7
* Django 1.10
~ 快速开始
```
pip install -r requirements.txt # Install pip module
yum -y install `cat rpm_requirements.txt` # Install rpm package
cp config_example.py config.py # Prepaire config from example config
cd apps && python manage.py makemigrations # Make migrations for django
python manage.py migrate # Migrate ORM to database
python manage.py loaddata init # Init some data
python manage.py loaddata fake # Generake some fake data
yum -y install redis && service redis start # Or install redis docker
python manage.py runserver 0.0.0.0:80 # Run it
```
~ 文档
* [项目结构描述](https://code.jumpserver.org/jumpserver/jumpserver/blob/master/docs/project_structure.md)
* [Python代码规范](https://code.jumpserver.org/jumpserver/jumpserver/blob/master/docs/python_style_guide.md)
* [API设计规范](https://code.jumpserver.org/jumpserver/jumpserver/blob/master/docs/api_style_guide.md)
* [表结构](https://code.jumpserver.org/Jumpserver/jumpserver/wikis/table-structure)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
if __name__ == '__main__':
pass
from django.contrib import admin
# Register your models here.
# -*- coding: utf-8 -*-
#
from collections import OrderedDict
from django.core.cache import cache
from django.conf import settings
from django.utils import timezone
import copy
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
from rest_framework import viewsets
from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny
from django.shortcuts import get_object_or_404
from rest_framework.decorators import api_view
from .models import Terminal, TerminalHeatbeat
from .serializers import TerminalSerializer, TerminalHeatbeatSerializer
from .hands import IsSuperUserOrAppUser, IsAppUser, User, ProxyLog
from common.utils import get_object_or_none
class TerminalRegisterView(ListCreateAPIView):
queryset = Terminal.objects.all()
serializer_class = TerminalSerializer
permission_classes = (AllowAny,)
def create(self, request, *args, **kwargs):
name = request.data.get('name', '')
remote_addr = request.META.get('X-Real-IP') or \
request.META.get('REMOTE_ADDR')
serializer = self.serializer_class(
data={'name': name, 'remote_addr': remote_addr})
if get_object_or_none(Terminal, name=name):
return Response({'msg': 'Already register, Need '
'administrator active it'}, status=200)
if serializer.is_valid():
terminal = serializer.save()
app_user, access_key = terminal.create_related_app_user()
data = OrderedDict()
data['terminal'] = copy.deepcopy(serializer.data)
data['user'] = app_user.to_json()
data['access_key_id'] = access_key.id
data['access_key_secret'] = access_key.secret
return Response(data, status=201)
else:
data = {'msg': 'Not valid', 'detail': ';'.join(serializer.errors)}
return Response(data, status=400)
def list(self, request, *args, **kwargs):
return Response('', status=404)
class TerminalViewSet(viewsets.ModelViewSet):
queryset = Terminal.objects.all()
serializer_class = TerminalSerializer
permission_classes = (IsSuperUserOrAppUser,)
def create(self, request, *args, **kwargs):
return Response({'msg': 'Use register view except that'}, status=404)
# def destroy(self, request, *args, **kwargs):
# instance = self.get_object()
# if instance.user is not None:
# instance.user.delete()
# return super(TerminalViewSet, self).destroy(request, *args, **kwargs)
tasks = OrderedDict()
# tasks = {1: [{'name': 'kill_proxy', 'proxy_log_id': 23}]}
class TerminalHeatbeatViewSet(viewsets.ModelViewSet):
queryset = TerminalHeatbeat.objects.all()
serializer_class = TerminalHeatbeatSerializer
permission_classes = (IsAppUser,)
def create(self, request, *args, **kwargs):
terminal = request.user.terminal
TerminalHeatbeat.objects.create(terminal=terminal)
task = tasks.get(terminal.name)
tasks[terminal.name] = []
return Response({'msg': 'Success',
'tasks': task},
status=201)
class TerminateConnectionView(APIView):
def post(self, request, *args, **kwargs):
if isinstance(request.data, dict):
data = [request.data]
else:
data = request.data
for d in data:
proxy_log_id = d.get('proxy_log_id')
proxy_log = get_object_or_404(ProxyLog, id=proxy_log_id)
terminal_id = proxy_log.terminal
proxy_log.is_finished = True
proxy_log.date_finished = timezone.now()
proxy_log.save()
if terminal_id in tasks:
tasks[terminal_id].append({'name': 'kill_proxy',
'proxy_log_id': proxy_log_id})
else:
tasks[terminal_id] = [{'name': 'kill_proxy',
'proxy_log_id': proxy_log_id}]
print(tasks)
return Response({'msg': 'get it'})
from __future__ import unicode_literals
from django.apps import AppConfig
class ApplicationsConfig(AppConfig):
name = 'applications'
# ~*~ coding: utf-8 ~*~
#
from django import forms
from django.utils.translation import ungettext_lazy as _
from .models import Terminal
class TerminalForm(forms.ModelForm):
class Meta:
model = Terminal
fields = ['name', 'remote_addr', 'type', 'url', 'comment']
help_texts = {
'url': 'Example: ssh://192.168.1.1:22 or http://jms.jumpserver.org, that user login'
}
widgets = {
'name': forms.TextInput(attrs={'readonly': 'readonly'})
}
\ No newline at end of file
# -*- coding: utf-8 -*-
#
from users.models import User
from users.permissions import IsSuperUserOrAppUser, IsAppUser
from audits.models import ProxyLog
\ No newline at end of file
from __future__ import unicode_literals
from django.db import models
from django.utils.translation import ugettext_lazy as _
from users.models import User
class Terminal(models.Model):
TYPE_CHOICES = (
('SSH', 'SSH Terminal'),
('Web', 'Web Terminal')
)
name = models.CharField(max_length=30, unique=True, verbose_name=_('Name'))
remote_addr = models.GenericIPAddressField(verbose_name=_('Remote address'), blank=True, null=True)
type = models.CharField(choices=TYPE_CHOICES, max_length=3, blank=True, verbose_name=_('Terminal type'))
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application user',
null=True, on_delete=models.CASCADE)
url = models.CharField(max_length=100, blank=True, verbose_name=_('URL to login'))
is_accepted = models.BooleanField(default=False, verbose_name='Is Accepted')
date_created = models.DateTimeField(auto_now_add=True)
comment = models.TextField(blank=True, verbose_name=_('Comment'))
@property
def is_active(self):
if self.user and self.user.is_active:
return True
return False
@is_active.setter
def is_active(self, active):
if self.user:
self.user.is_active = active
self.user.save()
def create_related_app_user(self):
user, access_key = User.create_app_user(name=self.name, comment=self.comment)
self.user = user
self.save()
return user, access_key
def delete(self, using=None, keep_parents=False):
if self.user:
self.user.delete()
return super(Terminal, self).delete(using=using, keep_parents=keep_parents)
def __unicode__(self):
active = 'Active' if self.user and self.user.is_active else 'Disabled'
return '%s: %s' % (self.name, active)
__str__ = __unicode__
class Meta:
ordering = ('is_accepted',)
class TerminalHeatbeat(models.Model):
terminal = models.ForeignKey(Terminal, on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'terminal_heatbeat'
# -*- coding: utf-8 -*-
#
from django.utils import timezone
from rest_framework import serializers
from .models import Terminal, TerminalHeatbeat
from .hands import ProxyLog
class TerminalSerializer(serializers.ModelSerializer):
proxy_online = serializers.SerializerMethodField()
is_alive = serializers.SerializerMethodField()
class Meta:
model = Terminal
fields = ['id', 'name', 'remote_addr', 'type', 'url', 'comment',
'is_accepted', 'is_active', 'get_type_display',
'proxy_online', 'is_alive']
@staticmethod
def get_proxy_online(obj):
return ProxyLog.objects.filter(terminal=obj.name, is_finished=False).count()
@staticmethod
def get_is_alive(obj):
log = obj.terminalheatbeat_set.last()
if log and timezone.now() - log.date_created < timezone.timedelta(seconds=600):
return True
else:
return False
class TerminalHeatbeatSerializer(serializers.ModelSerializer):
date_start = serializers.DateTimeField
class Meta:
model = TerminalHeatbeat
if __name__ == '__main__':
pass
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li class="active">
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Terminal detail' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'applications:terminal-update' pk=terminal.id %}"><i class="fa fa-edit"></i>Update</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-7" style="padding-left: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span class="label"><b>{{ terminal.name }}</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">
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="20%">{% trans 'Name' %}:</td>
<td><b>{{ terminal.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Remote addr' %}:</td>
<td><b>{{ terminal.remote_addr }}</b></td>
</tr>
<tr>
<td>{% trans 'Terminal url' %}:</td>
<td><b>{{ terminal.url }}</b></td>
</tr>
<tr>
<td>{% trans 'Terminal type' %}:</td>
<td><b>{{ terminal.get_type_display }}</b></td>
</tr>
<tr>
<td>{% trans 'Date created' %}:</td>
<td><b>{{ terminal.date_created }}</b></td>
</tr>
<tr>
<td>{% trans 'Comment' %}:</td>
<td><b>{{ asset.comment }}</b></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
{{ block.super }}
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: right !important;
}
div.dataTables_wrapper div.dataTables_filter {
margin-left: 15px;
}
#modal .modal-body { max-height: 200px; }
</style>
{% endblock %}
{% block table_search %}{% endblock %}
{% block table_container %}
{#<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="terminal_list_table" >
<thead>
<tr>
<th class="text-center">
<div class="checkbox checkbox-default">
<input type="checkbox" class="ipt_check_all">
</div>
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'IP' %}</th>
<th class="text-center">{% trans 'Type' %}</th>
<th class="text-center">{% trans 'proxy online' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Alive' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
{% include 'applications/terminal_modal_accept.html' %}
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#terminal_list_table'),
buttons: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "applications:terminal-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>')
} else {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData) {
if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
console.log(rowData.name);
var update_btn = '<a href="{% url "applications:terminal-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'
.replace('99991937', cellData);
var delete_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_delete" data-uid="99991937" data-name="99991938">{% trans "Delete" %}</a>'
.replace('99991937', cellData)
.replace('99991938', rowData.name);
var accept_btn = '<a class="btn btn-xs btn-primary btn-accept" data-id="99991937">{% trans "Accept" %}</a> '
.replace('99991937', cellData);
var reject_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_delete" data-uid="99991937" data-name="99991938">{% trans "Reject" %}</a>'
.replace('99991937', cellData)
.replace('99991938', rowData.name);
if (rowData.is_accepted) {
$(td).html(update_btn + delete_btn)
} else {
$(td).html(accept_btn + reject_btn)
}
}}
],
ajax_url: '{% url "api-applications:terminal-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "remote_addr" }, {data: "get_type_display" },
{data: "proxy_online"}, {data: "is_active" }, {data: 'is_active'}, {data: "id"}],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
$('#btn_terminal_accept').click(function () {
var $form = $('#form_terminal_accept');
function success(data, textStatus, jqXHR) {
if (data.success === true) {
window.location.reload()
} else {
$('#modal-error').html(data.msg).css('display', 'block');
}
}
$form.ajaxSubmit({success: success});
})
}).on('click', '.btn_delete', function(){
var $this = $(this);
var uid = $this.data('uid');
var name = $(this).data('name');
var the_url = '{% url "api-applications:terminal-detail" pk=99991937 %}'.replace('99991937', uid);
objectDelete($this, name, the_url)
}).on('click', '.btn-accept', function () {
var $this = $(this);
var terminal_id = $this.data('id');
var the_url = "{% url 'api-applications:terminal-detail' pk=99991937 %}".replace('99991937', terminal_id);
var post_url = $('#form_terminal_accept').attr('action').replace('99991937', terminal_id);
console.log(post_url);
$.ajax({
url: the_url,
method: 'GET',
success: function (data) {
$('#id_name').val(data.name);
$('#id_remote_addr').val(data.remote_addr);
$('#id_type').val(data.type);
$('#id_url').val(data.url);
$('#id_comment').val(data.comment);
$('#form_terminal_accept').attr('action', post_url)
}
});
$('#modal_terminal_accept').modal({
show: true
});
})
</script>
{% endblock %}
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}modal_terminal_accept{% endblock %}
{% block modal_class %}modal-lg{% endblock %}
{% block modal_title%}{% trans "Accept terminal registration" %}{% endblock %}
{% block modal_body %}
{% load bootstrap %}
<form action="{% url 'applications:terminal-modal-accept' pk="99991937" %}" method="post" class="form-horizontal" id="form_terminal_accept" enctype="multipart/form-data">
{% csrf_token %}
<p class="alert alert-danger" id="modal-error" style="display: none"></p>
{{ form.name|bootstrap_horizontal }}
{{ form.remote_addr|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
{{ form.url|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
</form>
{% endblock %}
{% block modal_confirm_id %}btn_terminal_accept{% endblock %}
\ No newline at end of file
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
\ No newline at end of file
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{{ action }}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Info' %}</h3>
{{ form.name|bootstrap_horizontal }}
{{ form.remote_addr|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
{{ form.url|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
from django.test import TestCase
# Create your tests here.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from django.conf.urls import url
from rest_framework import routers
from .. import api
app_name = 'applications'
router = routers.DefaultRouter()
router.register(r'v1/terminal/heatbeat', api.TerminalHeatbeatViewSet, 'terminal-heatbeat')
router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
urlpatterns = [
url(r'^v1/terminal/register/$', api.TerminalRegisterView.as_view(),
name='terminal-register'),
url(r'^v1/terminate/connection/$', api.TerminateConnectionView.as_view(),
name='terminate-connection')
# url(r'^v1/terminal/heatbeat/$', api.TestHeatbeat.as_view())
]
urlpatterns += router.urls
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from django.conf.urls import url
from .. import views
app_name = 'applications'
urlpatterns = [
url(r'^terminal$', views.TerminalListView.as_view(), name='terminal-list'),
url(r'^terminal/(?P<pk>\d+)/$', views.TerminalDetailView.as_view(),
name='terminal-detail'),
url(r'^terminal/(?P<pk>\d+)/update$', views.TerminalUpdateView.as_view(),
name='terminal-update'),
url(r'^terminal/(?P<pk>\d+)/modal/accept$', views.TerminalModelAccept.as_view(),
name='terminal-modal-accept'),
]
# ~*~ coding: utf-8 ~*~
#
from django.views.generic import ListView, UpdateView, DeleteView, DetailView
from django.views.generic.edit import BaseUpdateView
from django.utils.translation import ugettext as _
from django.urls import reverse_lazy
from .models import Terminal
from users.utils import AdminUserRequiredMixin
from common.mixins import JSONResponseMixin
from .forms import TerminalForm
class TerminalListView(ListView):
model = Terminal
template_name = 'applications/terminal_list.html'
form_class = TerminalForm
def get_context_data(self, **kwargs):
context = super(TerminalListView, self).get_context_data(**kwargs)
context.update({
'app': _('Terminal'),
'action': _('Terminal list'),
'form': self.form_class()
})
return context
class TerminalUpdateView(UpdateView):
model = Terminal
form_class = TerminalForm
template_name = 'applications/terminal_update.html'
success_url = reverse_lazy('applications:applications-list')
def get_context_data(self, **kwargs):
context = super(TerminalUpdateView, self).get_context_data(**kwargs)
context.update({'app': _('Applications'), 'action': _('Update terminal')})
return context
class TerminalDetailView(DetailView):
model = Terminal
template_name = 'applications/terminal_detail.html'
context_object_name = 'terminal'
def get_context_data(self, **kwargs):
context = super(TerminalDetailView, self).get_context_data(**kwargs)
context.update({
'app': _('Applications'),
'action': _('Terminal detail')
})
return context
class TerminalDeleteView(DeleteView):
model = Terminal
template_name = 'assets/delete_confirm.html'
success_url = reverse_lazy('applications:applications-list')
class TerminalModelAccept(AdminUserRequiredMixin, JSONResponseMixin, UpdateView):
model = Terminal
form_class = TerminalForm
template_name = 'applications/terminal_modal_test.html'
def post(self, request, *args, **kwargs):
print(request.POST)
return super(TerminalModelAccept, self).post(request, *args, **kwargs)
def form_valid(self, form):
terminal = form.save()
terminal.is_accepted = True
terminal.is_active = True
terminal.save()
data = {
'success': True,
'msg': 'success'
}
return self.render_json_response(data)
def form_invalid(self, form):
print('form.data')
data = {
'success': False,
'msg': str(form.errors),
}
return self.render_json_response(data)
# ~*~ coding: utf-8 ~*~
from rest_framework import viewsets, generics, mixins
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_bulk import BulkModelViewSet, BulkDestroyAPIView
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
from django.shortcuts import get_object_or_404
from common.mixins import IDInFilterMixin
from common.utils import get_object_or_none, signer
from .hands import IsSuperUser, IsAppUser
from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser, Tag
from . import serializers
class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
"""API endpoint that allows Asset to be viewed or edited."""
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
def get_queryset(self):
queryset = super(AssetViewSet, self).get_queryset()
idc_id = self.request.query_params.get('idc_id', '')
tags_id = self.request.query_params.get('tag_id', '')
system_users_id = self.request.query_params.get('system_user_id', '')
asset_group_id = self.request.query_params.get('asset_group_id', '')
admin_user_id = self.request.query_params.get('admin_user_id', '')
if idc_id:
queryset = queryset.filter(idc__id=idc_id)
if tags_id:
queryset = queryset.filter(tags__id=tags_id)
if system_users_id:
queryset = queryset.filter(system_users__id=system_users_id)
if admin_user_id:
queryset = queryset.filter(admin_user__id=admin_user_id)
if asset_group_id:
queryset = queryset.filter(groups__id=asset_group_id)
return queryset
class AssetGroupViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = AssetGroup.objects.all()
serializer_class = serializers.AssetGroupSerializer
permission_classes = (IsSuperUser,)
class AssetUpdateGroupApi(generics.RetrieveUpdateAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetUpdateGroupSerializer
permission_classes = (IsSuperUser,)
## update the asset group, and add or delete the asset to the group
class AssetGroupUpdateApi(generics.RetrieveUpdateAPIView):
queryset = AssetGroup.objects.all()
serializer_class = serializers.AssetGroupUpdateSerializer
permission_classes = (IsSuperUser,)
## update the asset group, and add or delete the system_user to the group
class AssetGroupUpdateSystemUserApi(generics.RetrieveUpdateAPIView):
queryset = AssetGroup.objects.all()
serializer_class = serializers.AssetGroupUpdateSystemUserSerializer
permission_classes = (IsSuperUser,)
## update the IDC, and add or delete the assets to the IDC
class IDCupdateAssetsApi(generics.RetrieveUpdateAPIView):
queryset = IDC.objects.all()
serializer_class = serializers.IDCUpdateAssetsSerializer
permission_classes = (IsSuperUser,)
class IDCViewSet(IDInFilterMixin, BulkModelViewSet):
"""API endpoint that allows IDC to be viewed or edited."""
queryset = IDC.objects.all()
serializer_class = serializers.IDCSerializer
permission_classes = (IsSuperUser,)
class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = AdminUser.objects.all()
serializer_class = serializers.AdminUserSerializer
permission_classes = (IsSuperUser,)
class SystemUserViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = SystemUser.objects.all()
serializer_class = serializers.SystemUserSerializer
permission_classes = (IsSuperUser,)
class SystemUserUpdateApi(generics.RetrieveUpdateAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetUpdateSystemUserSerializer
permission_classes = (IsSuperUser,)
class SystemUserUpdateAssetsApi(generics.RetrieveUpdateAPIView):
queryset = SystemUser.objects.all()
serializer_class = serializers.SystemUserUpdateAssetsSerializer
permission_classes = (IsSuperUser,)
class SystemUserUpdateAssetGroupApi(generics.RetrieveUpdateAPIView):
queryset = SystemUser.objects.all()
serializer_class = serializers.SystemUserUpdateAssetGroupSerializer
permission_classes = (IsSuperUser,)
# class IDCAssetsApi(generics.ListAPIView):
# model = IDC
# serializer_class = serializers.AssetSerializer
#
# def get(self, request, *args, **kwargs):
# filter_kwargs = {self.lookup_field: self.kwargs[self.lookup_field]}
# self.object = get_object_or_404(self.model, **filter_kwargs)
# return super(IDCAssetsApi, self).get(request, *args, **kwargs)
#
# def get_queryset(self):
# return self.object.assets.all()
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
class SystemUserAuthInfoApi(generics.RetrieveAPIView):
queryset = SystemUser.objects.all()
permission_classes = (IsAppUser,)
def retrieve(self, request, *args, **kwargs):
system_user = self.get_object()
data = {
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
'password': system_user.password,
'private_key': system_user.private_key,
'auth_method': system_user.auth_method,
}
return Response(data)
class TagViewSet(IDInFilterMixin, BulkModelViewSet):
queryset = Tag.objects.all()
serializer_class = serializers.TagSerializer
permission_classes = (IsSuperUser,)
## update the IDC, and add or delete the assets to the IDC
class TagUpdateAssetsApi(generics.RetrieveUpdateAPIView):
queryset = Tag.objects.all()
serializer_class = serializers.TagUpdateAssetsSerializer
permission_classes = (IsSuperUser,)
from __future__ import unicode_literals
from django.apps import AppConfig
class AssetsConfig(AppConfig):
name = 'assets'
This diff is collapsed.
"""
jumpserver.__app__.hands.py
~~~~~~~~~~~~~~~~~
This app depends other apps api, function .. should be import or write mack here.
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2016 by Jumpserver Team.
:license: GPL v2, see LICENSE for more details.
"""
from users.utils import AdminUserRequiredMixin
from users.permissions import IsAppUser, IsSuperUser
from users.models import User, UserGroup
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from .idc import *
from .user import *
from .group import *
from .asset import *
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals
from django.db import models
import logging
from django.utils.translation import ugettext_lazy as _
from . import IDC, AssetGroup, AdminUser, SystemUser
__all__ = ['Asset', 'Tag']
logger = logging.getLogger(__name__)
def get_default_idc():
return IDC.initial()
class Asset(models.Model):
STATUS_CHOICES = (
('In use', _('In use')),
('Out of use', _('Out of use')),
)
TYPE_CHOICES = (
('Server', _('Server')),
('VM', _('VM')),
('Switch', _('Switch')),
('Router', _('Router')),
('Firewall', _('Firewall')),
('Storage', _("Storage")),
)
ENV_CHOICES = (
('Prod', 'Production'),
('Dev', 'Development'),
('Test', 'Testing'),
)
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
other_ip = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Other IP'))
remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote card IP'))
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups'))
admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_("Admin user"))
system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User"))
idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets',
on_delete=models.SET_NULL, verbose_name=_('IDC'),)
mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address"))
brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand'))
cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU'))
memory = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Memory'))
disk = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk'))
os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS'))
cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number'))
cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position'))
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
status = models.CharField(choices=STATUS_CHOICES, max_length=8, null=True, blank=True,
default='In use', verbose_name=_('Asset status'))
type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True,
default='Server', verbose_name=_('Asset type'),)
env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True,
default='Prod', verbose_name=_('Asset environment'),)
sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Serial number'))
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date added'))
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
tags = models.ManyToManyField('Tag', related_name='assets', blank=True, verbose_name=_('Tags'))
def __unicode__(self):
return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port}
__str__ = __unicode__
@property
def is_valid(self):
warning = ''
if not self.is_active:
warning += ' inactive'
else:
return True, ''
return False, warning
def to_json(self):
pass
class Meta:
unique_together = ('ip', 'port')
@classmethod
def generate_fake(cls, count=100):
from random import seed, choice
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
asset = cls(ip='%s.%s.%s.%s' % (i, i, i, i),
hostname=forgery_py.internet.user_name(True),
admin_user=choice(AdminUser.objects.all()),
idc=choice(IDC.objects.all()),
port=22,
created_by='Fake')
try:
asset.save()
asset.system_users = [choice(SystemUser.objects.all()) for i in range(3)]
asset.groups = [choice(AssetGroup.objects.all()) for i in range(3)]
logger.debug('Generate fake asset : %s' % asset.ip)
except IntegrityError:
print('Error continue')
continue
class Tag(models.Model):
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
created_time = models.DateTimeField(auto_now_add=True, verbose_name=_('Create time'))
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
def __unicode__(self):
return self.name
__str__ = __unicode__
class Meta:
db_table = 'tag'
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals
from django.db import models
import logging
from django.utils.translation import ugettext_lazy as _
from . import SystemUser
__all__ = ['AssetGroup']
logger = logging.getLogger(__name__)
class AssetGroup(models.Model):
name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
system_users = models.ManyToManyField(SystemUser, related_name='asset_groups', blank=True)
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date added'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
class Meta:
ordering = ['name']
@classmethod
def initial(cls):
asset_group = cls(name=_('Default'), comment=_('Default asset group'))
asset_group.save()
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
group = cls(name=forgery_py.name.full_name(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
group.save()
logger.debug('Generate fake asset group: %s' % group.name)
except IntegrityError:
print('Error continue')
continue
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals
from django.db import models
import logging
from django.utils.translation import ugettext_lazy as _
__all__ = ['IDC']
logger = logging.getLogger(__name__)
class IDC(models.Model):
name = models.CharField(max_length=32, verbose_name=_('Name'))
bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth'))
contact = models.CharField(max_length=16, blank=True, verbose_name=_('Contact'))
phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone'))
address = models.CharField(max_length=128, blank=True, verbose_name=_("Address"))
intranet = models.TextField(blank=True, verbose_name=_('Intranet'))
extranet = models.TextField(blank=True, verbose_name=_('Extranet'))
date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date added'))
operator = models.CharField(max_length=32, blank=True, verbose_name=_('Operator'))
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
@classmethod
def initial(cls):
return cls.objects.get_or_create(name=_('Default'), created_by=_('System'), comment=_('Default IDC'))[0]
class Meta:
ordering = ['name']
@classmethod
def generate_fake(cls, count=100):
from random import seed, choice
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
idc = cls(name=forgery_py.name.full_name(),
bandwidth='200M',
contact=forgery_py.name.full_name(),
phone=forgery_py.address.phone(),
address=forgery_py.address.city() + forgery_py.address.street_address(),
operator=choice(['北京联通', '北京电信', 'BGP全网通']),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
idc.save()
logger.debug('Generate fake asset group: %s' % idc.name)
except IntegrityError:
print('Error continue')
continue
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals
from django.db import models
import logging
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from common.utils import signer, validate_ssh_private_key
__all__ = ['AdminUser', 'SystemUser', 'private_key_validator']
logger = logging.getLogger(__name__)
def private_key_validator(value):
if not validate_ssh_private_key(value):
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
class AdminUser(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.CharField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'),
validators=[private_key_validator,])
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
def __unicode__(self):
return self.name
__str__ = __unicode__
@property
def password(self):
return signer.unsign(self._password)
@password.setter
def password(self, password_raw):
self._password = signer.sign(password_raw)
@property
def private_key(self):
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = signer.sign(private_key_raw)
@property
def public_key(self):
return signer.unsign(self._public_key)
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw)
@property
def assets_amount(self):
return self.assets.count()
class Meta:
ordering = ['name']
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
obj = cls(name=forgery_py.name.full_name(),
username=forgery_py.internet.user_name(),
password=forgery_py.lorem_ipsum.word(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
obj.save()
logger.debug('Generate fake asset group: %s' % obj.name)
except IntegrityError:
print('Error continue')
continue
class SystemUser(models.Model):
PROTOCOL_CHOICES = (
('ssh', 'ssh'),
)
AUTH_METHOD_CHOICES = (
('P', 'Password'),
('K', 'Public key'),
)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K',
max_length=1, verbose_name=_('Auth method'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
home = models.CharField(max_length=64, blank=True, verbose_name=_('Home'))
uid = models.IntegerField(null=True, blank=True, verbose_name=_('Uid'))
date_created = models.DateTimeField(auto_now_add=True)
created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by'))
comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment'))
def __unicode__(self):
return self.name
@property
def password(self):
return signer.unsign(self._password)
@password.setter
def password(self, password_raw):
self._password = signer.sign(password_raw)
@property
def private_key(self):
return signer.unsign(self._private_key)
@private_key.setter
def private_key(self, private_key_raw):
self._private_key = signer.sign(private_key_raw)
@property
def public_key(self):
return signer.unsign(self._public_key)
@public_key.setter
def public_key(self, public_key_raw):
self._public_key = signer.sign(public_key_raw)
def get_assets_inherit_from_asset_groups(self):
assets = set()
asset_groups = self.asset_groups.all()
for asset_group in asset_groups:
for asset in asset_group.assets.all():
setattr(asset, 'is_inherit_from_asset_groups', True)
setattr(asset, 'inherit_from_asset_groups',
getattr(asset, b'inherit_from_asset_groups', set()).add(asset_group))
assets.add(asset)
return assets
def get_assets(self):
assets = set(self.assets.all()) | self.get_assets_inherit_from_asset_groups()
return list(assets)
@property
def assets_amount(self):
return self.assets.count()
@property
def asset_group_amount(self):
return self.asset_groups.count()
def to_json(self):
return {
'id': self.id,
'name': self.name,
'username': self.username,
'protocol': self.protocol,
'auth_method': self.auth_method,
'auto_push': self.auto_push,
}
class Meta:
ordering = ['name']
@classmethod
def generate_fake(cls, count=100):
from random import seed
import forgery_py
from django.db import IntegrityError
seed()
for i in range(count):
obj = cls(name=forgery_py.name.full_name(),
username=forgery_py.internet.user_name(),
password=forgery_py.lorem_ipsum.word(),
comment=forgery_py.lorem_ipsum.sentence(),
created_by='Fake')
try:
obj.save()
logger.debug('Generate fake asset group: %s' % obj.name)
except IntegrityError:
print('Error continue')
continue
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from . import IDC, SystemUser, AdminUser, AssetGroup, Asset, Tag
__all__ = ['initial', 'generate_fake']
def initial():
for cls in [IDC, SystemUser, AdminUser, AssetGroup, Asset, Tag]:
if hasattr(cls, 'initial'):
cls.initial()
def generate_fake():
for cls in [IDC, SystemUser, AdminUser, AssetGroup, Asset, Tag]:
if hasattr(cls, 'generate_fake'):
cls.generate_fake()
if __name__ == '__main__':
pass
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
from rest_framework import viewsets, serializers,generics
from .models import AssetGroup, Asset, IDC, AdminUser, SystemUser, Tag
from common.mixins import IDInFilterMixin
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
class AssetGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
assets_amount = serializers.SerializerMethodField()
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = AssetGroup
list_serializer_class = BulkListSerializer
fields = ['id', 'name', 'comment', 'assets_amount', 'assets']
@staticmethod
def get_assets_amount(obj):
return obj.assets.count()
class AssetUpdateGroupSerializer(serializers.ModelSerializer):
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
class Meta:
model = Asset
fields = ['id', 'groups']
class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
system_users = serializers.PrimaryKeyRelatedField(many=True, queryset=SystemUser.objects.all())
class Meta:
model = Asset
fields = ['id', 'system_users']
class AssetGroupUpdateSerializer(serializers.ModelSerializer):
"""update the asset group, and add or delete the asset to the group"""
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = AssetGroup
fields = ['id', 'assets']
class AssetGroupUpdateSystemUserSerializer(serializers.ModelSerializer):
system_users = serializers.PrimaryKeyRelatedField(many=True, queryset=SystemUser.objects.all())
class Meta:
model = AssetGroup
fields = ['id', 'system_users']
class IDCUpdateAssetsSerializer(serializers.ModelSerializer):
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = IDC
fields = ['id', 'assets']
class TagSerializer(BulkSerializerMixin, serializers.ModelSerializer):
assets_amount = serializers.SerializerMethodField()
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = Tag
list_serializer_class = BulkListSerializer
fields = '__all__'
@staticmethod
def get_assets_amount(obj):
return obj.assets.count()
class AdminUserSerializer(serializers.ModelSerializer):
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = AdminUser
fields = '__all__'
def get_field_names(self, declared_fields, info):
fields = super(AdminUserSerializer, self).get_field_names(declared_fields, info)
fields.append('assets_amount')
return fields
class SystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
exclude = ('_password', '_private_key', '_public_key')
def get_field_names(self, declared_fields, info):
fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info)
fields.extend(['assets_amount'])
return fields
class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ('id', 'name', 'username', 'protocol', 'auth_method', 'comment')
class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer):
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = SystemUser
fields = ['id', 'assets']
class SystemUserUpdateAssetGroupSerializer(serializers.ModelSerializer):
asset_groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
class Meta:
model = SystemUser
fields = ['id', 'asset_groups']
class SystemUserSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ('id', 'name', 'username')
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# system_users = SystemUserSerializer(many=True, read_only=True)
# admin_user = AdminUserSerializer(many=False, read_only=True)
hardware = serializers.SerializerMethodField()
class Meta(object):
model = Asset
list_serializer_class = BulkListSerializer
fields = '__all__'
@staticmethod
def get_hardware(obj):
if obj.cpu:
return '%s %s %s' % (obj.cpu, obj.memory, obj.disk)
else:
return ''
def get_field_names(self, declared_fields, info):
fields = super(AssetSerializer, self).get_field_names(declared_fields, info)
fields.extend(['get_type_display', 'get_env_display'])
return fields
class AssetGrantedSerializer(serializers.ModelSerializer):
system_users_granted = AssetSystemUserSerializer(many=True, read_only=True)
is_inherited = serializers.SerializerMethodField()
system_users_join = serializers.SerializerMethodField()
class Meta(object):
model = Asset
fields = ("id", "hostname", "ip", "port", "system_users_granted", "is_inherited",
"is_active", "system_users_join", "comment")
@staticmethod
def get_is_inherited(obj):
if getattr(obj, 'inherited', ''):
return True
else:
return False
@staticmethod
def get_system_users_join(obj):
return ', '.join([system_user.username for system_user in obj.system_users_granted])
class IDCSerializer(BulkSerializerMixin, serializers.ModelSerializer):
assets_amount = serializers.SerializerMethodField()
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = IDC
fields = '__all__'
@staticmethod
def get_assets_amount(obj):
return obj.assets.count()
def get_field_names(self, declared_fields, info):
fields = super(IDCSerializer, self).get_field_names(declared_fields, info)
fields.append('assets_amount')
return fields
class TagUpdateAssetsSerializer(serializers.ModelSerializer):
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
class Meta:
model = Tag
fields = ['id', 'assets']
\ No newline at end of file
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}asset_bulk_update_modal{% endblock %}
{% block modal_class %}modal-lg{% endblock %}
{% block modal_title%}{% trans "Update Asset" %}{% endblock %}
{% block modal_body %}
{% load bootstrap %}
<p class="text-success text-center">{% trans "Hint: only change the field you want to update." %}</p>
<div class="ydxbd" id="ydxbd" style="display: block;">
<div>
<p id="tags_p">
<a href="/assets/asset-by-tag/5">
<span class="label label-default">三年质保(0)</span>
</a>
</p>
</div>
</div>
<form method="post" class="form-horizontal" action="" id="fm_asset_bulk_update">
<div class="form-group">
<label class="control-label col-sm-2 col-lg-2 " for="id_type">{% trans "System Type" %}</label>
<div class=" col-sm-9 col-lg-9 ">
<select class=" select2 form-control" id="id_type" name="type">
<option value="">---------</option>
<option value="Server">{% trans "Server" %}</option>
<option value="VM">{% trans "VM" %}</option>
<option value="Switch">{% trans "Switch" %}</option>
<option value="Storage">{% trans "Storage" %}</option>
<option value="Router">{% trans "Router" %}</option>
<option value="Firewall">{% trans "Firewall" %}</option>
</select>
</div>
</div>
<div class="form-group">
<label for="groups" class="col-sm-2 control-label">{% trans 'Asset Groups' %}</label>
<div class="col-sm-9" id="select2-container">
<select name="groups" id="select2_groups" data-placeholder="{% trans 'Select Group' %}" class="select2 form-control m-b" multiple>
{% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label for="users" class="col-sm-2 control-label">{% trans 'System Users' %}</label>
<div class="col-sm-9" id="select2-container">
<select name="system_users" id="select2_users" data-placeholder="{% trans 'Select System Users' %}" class="select2 form-control m-b" multiple>
{% for system_user in system_users %}
<option value="{{ system_user.id }}">{{ system_user.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-lg-9 col-sm-offset-2">
<div class="checkbox checkbox-success">
<input type="checkbox" name="enable_otp" checked id="id_enable_otp"><label for="id_enable_otp">{% trans 'Enable-OTP' %}</label>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-2 col-lg-2 " for="id_tags">标签集合</label>
<div class=" col-sm-9 col-lg-9 ">
<select multiple="multiple" class="select2 form-control" data-placeholder="Select asset tags" id="tags" name="tags">
{% for tag in tags %}
<option value="{{ tag.id }}">{{ tag.name }}</option>
{% endfor %}
</select>
<p class="help-block">
最多5个标签,单个标签最长8个汉字,按回车确认
</p>
</div>
</div>
</form>
{% endblock %}
{% block modal_confirm_id %}btn_asset_bulk_update{% endblock %}
\ No newline at end of file
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}asset_group_bulk_update_modal{% endblock %}
{% block modal_class %}modal-lg{% endblock %}
{% block modal_title%}{% trans "Update Asset Group" %}{% endblock %}
{% block modal_body %}
{% load bootstrap %}
<p class="text-success text-center">{% trans "Hint: only change the field you want to update." %}</p>
<form method="post" class="form-horizontal" action="" id="fm_asset_group_bulk_update">
<div class="form-group">
<label for="assets" class="col-sm-2 control-label">{% trans 'Assets' %}</label>
<div class="col-sm-9" id="select2-container">
<select name="assets" id="select2_groups" data-placeholder="{% trans 'Select Asset' %}" class="select2 form-control m-b" multiple>
{% for asset in assets %}
<option value="{{ asset.id }}">{{ asset.ip }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label for="system_users" class="col-sm-2 control-label">{% trans 'System Users' %}</label>
<div class="col-sm-9" id="select2-container">
<select name="system_users" id="select2_groups" data-placeholder="{% trans 'Select System Users' %}" class="select2 form-control m-b" multiple>
{% for system_user in system_users %}
<option value="{{ system_user.id }}">{{ system_user.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-9 col-lg-9 col-sm-offset-2">
<div class="checkbox checkbox-success">
<input type="checkbox" name="enable_otp" checked id="id_enable_otp"><label for="id_enable_otp">{% trans 'Enable-OTP' %}</label>
</div>
</div>
</div>
</form>
{% endblock %}
{% block modal_confirm_id %}btn_asset_group_bulk_update{% endblock %}
\ No newline at end of file
{% extends '_modal.html' %}
{% load i18n %}
{% block modal_id %}asset_import_modal{% endblock %}
{% block modal_title%}{% trans "Import asset" %}{% endblock %}
{% block modal_body %}
<p class="text-success">{% trans "Download template or use export excel format" %}</p>
<form method="post" action="{% url 'assets:asset-import' %}" id="fm_asset_import" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label class="control-label" for="id_assets">{% trans "Template" %}</label>
<a href="{{ MEDIA_URL }}files/asset_import_template.xlsx" style="display: block">{% trans 'Download' %}</a>
</div>
<div class="form-group">
<label class="control-label" for="id_users">{% trans "Asset excel file" %}</label>
<input id="id_assets" type="file" name="file" />
</div>
</form>
<p>
<p class="text-success" id="id_created"></p>
<p id="id_created_detail"></p>
<p class="text-warning" id="id_updated"></p>
<p id="id_updated_detail"></p>
<p class="text-danger" id="id_failed"></p>
<p id="id_failed_detail"></p>
</p>
{% endblock %}
{% block modal_confirm_id %}btn_asset_import{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create admin user' %}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
{{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }}
{{ form.password|bootstrap_horizontal }}
{{ form.private_key_file|bootstrap_horizontal }}
{{ form.assets|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
})
</script>
{% endblock %}
\ No newline at end of file
This diff is collapsed.
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5">
<a href="{% url "assets:admin-user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create admin user" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="admin_user_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 'Username' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Lost connection' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete 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 content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function(){
var options = {
ele: $('#admin_user_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 8 ? cellData.substring(0, 24) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
{# var script_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'.replace('99991937', cellData);#}
var update_btn = '<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
{# $(td).html(script_btn + update_btn + del_btn)#}
$(td).html(update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:admin-user-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" }, {data: function () {return 'lost'} },
{data: "comment" }, {data: "id" }],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
})
.on('click', '.btn_admin_user_delete', function () {
var $this = $(this);
var $data_table = $("#admin_user_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:admin-user-detail" pk=99991937 %}'.replace('99991937', uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
})
.on('click', '#btn_bulk_update', function () {
var action = $('#slct_bulk_update').val();
var $data_table = $('#admin_user_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (plain_id_list.length == 0) {
return false;
}
var the_url = "{% url 'api-assets:admin-user-list' %}";
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected Admin Users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'Admin Users Deleted.' %}";
swal("{% trans 'Admin Users Delete' %}", msg, "success");
$('#admin_user_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'Admin Users Deleting failed.' %}";
swal("{% trans 'Admin Users Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
}
switch (action) {
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
});
</script>
{% endblock %}
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap %}
{% load i18n %}
{% block form %}
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
{{ form.hostname|bootstrap_horizontal }}
{{ form.ip|bootstrap_horizontal }}
{{ form.port|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Group' %}</h3>
{{ form.idc|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Asset user' %}</h3>
{{ form.admin_user|bootstrap_horizontal }}
{{ form.system_users|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
{{ form.tags|bootstrap_horizontal }}
{{ form.comment|bootstrap_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>
$(document).ready(function () {
$('.select2').select2();
$("#id_tags").select2({
tags: true,
maximumSelectionLength: 8 //最多能够选择的个数
//closeOnSelect: false
});
})
</script>
{% endblock %}
\ No newline at end of file
This diff is collapsed.
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> {{ action }}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="panel blank-panel">
<div class="panel-body">
<div class="tab-content">
<div id="tab-1" class="ibox float-e-margins tab-pane active"></div>
<form id="groupForm" method="post" class="form-horizontal">
{% csrf_token %}
<h3 class="widget-head-color-box">资产组信息</h3>
{{ form.name|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">用户选择的资产</h3>
<div class="form-group">
<label class="col-sm-2 control-label" id="asset_on_count">已选({{ assets_count }})</label>
<div class="col-sm-9" id="asset_sed">
<div class="form-asset-on" id="add_asset">
<p id="asset_on_p">
{% for asset in assets_on_list %}
<button name='asset_hostname' title='{{ asset.ip }}' type='button' class='btn btn-default btn-xs'>{{ asset.hostname }}</button>
{% endfor %}
</p>
</div>
</div>
</div>
<div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">资产用户</h3>
{{ form.system_users|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-5">
<button class="btn btn-white" type="reset"> 重置 </button>
<button class="btn btn-primary" type="submit"> 提交 </button>
<div id='box2'> </div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 模态框(Modal) -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" id="box">
<!--此部分为主体内容,将远程加载进来-->
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
});
$('#add_asset').on('click',function(){
$('#modal').modal('show');
});
$('#modal').modal({
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}"
});
$('#modal').on('show.bs.modal',function(){
//alert('当调用show方法时,立即触发;')
});
$('#modal').on('shown.bs.modal',function(){
//alert('当弹窗完全加载完后,再触发;')
});
$('#modal').on('hide.bs.modal',function(){
//alert('当关闭时,立即触发;')
});
$('#modal').on('hidden.bs.modal',function(){
//alert('当关完全关闭后,再触发;')
});
$('#modal').on('loaded.bs.modal',function(){
//alert('当远程数据加载完毕后,再触发;')
});
</script>
{% endblock %}
\ No newline at end of file
This diff is collapsed.
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5">
<a href="{% url "assets:asset-group-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset group" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="asset_groups_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 'Asset' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update 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>
{% include 'assets/_asset_group_bulk_update_modal.html' %}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script>
$(document).ready(function(){
var options = {
ele: $('#asset_groups_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 3, createdCell: function (td, cellData) {
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
$(td).html('<a href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</a>');
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-group-update" pk=99991937 %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_group_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
$(td).html(update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:asset-group-list" %}',
columns: [{data: "id"}, {data: "name" }, {data: "assets_amount" }, {data: "comment" }, {data: "id"}],
op_html: $('#actions').html()
};
jumpserver.initDataTable(options);
})
.on('click', '.btn_asset_group_delete', function () {
var $this = $(this);
var $data_table = $('#asset_groups_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:asset-group-detail" pk=99991937 %}'.replace('99991937', uid);
objectDelete($this, name, the_url);
setTimeout( function () {
$data_table.ajax.reload();
}, 3000);
})
.on('click', '#btn_bulk_update', function () {
var action = $('#slct_bulk_update').val();
var $data_table = $('#asset_groups_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = '{% url "api-assets:asset-group-list" %}';
console.log(plain_id_list);
console.log(the_url);
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected groups !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'Group Deleted.' %}";
swal("{% trans 'Group Delete' %}", msg, "success");
$('#asset_groups_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'Group Deleting failed.' %}";
swal("{% trans 'Group Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
$('#asset_group_bulk_update_modal').modal('show');
}
switch(action) {
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
})
.on('click', '#btn_asset_group_bulk_update', function () {
var json_data = $("#fm_asset_group_bulk_update").serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.type != '') {
body.type = json_data.type;
}
if (json_data.assets != undefined) {
body.assets = json_data.assets;
}
if (typeof body.assets === 'string') {
body.assets = [parseInt(body.assets)]
} else if(typeof body.assets === 'array') {
var new_assets = body.assets.map(Number);
body.assets = new_assets;
}
if (json_data.system_users != undefined) {
body.system_users = json_data.system_users;
}
if (typeof body.system_users === 'string') {
body.system_users = [parseInt(body.system_users)];
} else if (typeof body.system_users === 'array') {
var new_system_users = body.system_users.map(Number);
body.system_users = new_system_users;
}
var post_list = [];
var $data_table = $('#asset_groups_list_table').DataTable()
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = '{% url "api-assets:asset-group-list" %}';
var success = function() {
var msg = "{% trans 'The selected asset groups has been updated successfully.' %}";
swal("{% trans 'AssetGroup Updated' %}", msg, "success");
$('#asset_groups_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
{# console.log(JSON.stringify(post_list));#}
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#asset_group_bulk_update_modal').modal('hide');
});
</script>
{% endblock %}
This diff is collapsed.
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title" id="myModalLabel">分配/回收资产</h4>
</div>
<div class="modal-body" style="padding-bottom: 0px;">
<table aria-describedby="editable_info" role="grid" class="table table-striped table-bordered table-hover dataTable" id="editable">
<thead>
<tr>
<th class="text-center" style="background-color:white">
<input type="checkbox" id="check_all" onclick="checkAll()">
</th>
<th id="th_no">id</th>
<th>资产名称</th>
<th>IP</th>
<th>硬件类型</th>
<th>资产组</th>
<th>部门</th>
</tr>
</thead>
<tbody>
{% for asset in asset_modal_list %}
{% if asset.id in all_assets %}
<tr name="oAssets" class="odd selected">
<td class="text-center" ><input type="checkbox" name="checked" value="{{ asset.id }}" checked="checked" ></td>
{% else %}
<tr name="oAssets">
<td class="text-center" ><input type="checkbox" name="checked" value="{{ asset.id }}" ></td>
{% endif %}
<td>{{ asset.id }}</td>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>虚拟机</td>
<td>网络设备</td>
<td>微信事业部</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" id="close-btn">取消</button>
<button type="button" class="btn btn-primary" id="save-btn">保存</button>
</div>
<script type="text/javascript">
$(document).ready(function(){
var table = $('#editable').DataTable({
"aLengthMenu": [[10, 25, 50, -1], ["10", "25", "50", "all"]],
"iDisplayLength":25,
"aaSorting": [[2, "asc"]],
"aoColumnDefs": [ { "bSortable": false, "aTargets": [ 0 ] }],
"bAutoWidth": false,
"language": {
"url": "/static/js/plugins/dataTables/i18n/zh-hans.json"
},
columns: [
{data: "checkbox"},
{data: "id"},
{data: "hostname"},
{data: "ip"},
{data: "type"},
{data: "group"},
{data: "dp"}
]
});
//将ID列隐藏
table.column('1').visible(false);
$('#editable tbody').on( 'click', 'tr', function () {
//alert($(this).hasClass('selected'));
if($(this).hasClass('selected')){
$(this).removeClass('selected');
this.children[0].children[0].checked=0;
}else{
$(this).addClass('selected');
this.children[0].children[0].checked=1;
};
});
$('#close-btn').on('click',function(){
$('#modal').modal('hide');
});
var size_name = document.getElementById('asset_on_count').innerText
$('#save-btn').on('click',function(){
//alert( table.rows('.selected').data().length +' row(s) selected' );
var d = table.rows('.selected').data();
var size = d.length;
var re = /\d+/
document.getElementById('add_asset').value = size;
var str= size_name;
var re=/\d+/g;
document.getElementById('asset_on_count').innerText = str.replace(re, size);
var column2 = table.rows('.selected').data();
$("#asset_sed").find("input[name='assets']").remove();
$("#asset_sed").find("button[name='asset_hostname']").remove();
for(var i=0;i<column2.length;i++){
column2[i].checkbox='<input name="checked" value="1" checked="" type="checkbox">';
var value = column2[i].id;
var ip = column2[i].ip;
var hostname = column2[i].hostname;
$("#asset_sed").append("<input type='hidden' name='assets' value='"+value+"'>");
$("#asset_on_p").append("<button name='asset_hostname' title='"+ip+"' type='button' class='btn btn-default btn-xs ss'>"+hostname+"</button> ");
}
$('#modal').modal('hide');
});
}); //$(document).ready
var bCheck = 1;
function checkAll(){
if(bCheck){
$("tr[name='oAssets']").each(function(){
oCheckbox = this.children[0].children[0];
$(this).toggleClass('selected',true);
oCheckbox.checked=1;
});
document.getElementById('check_all').checked=1;
bCheck = 0;
}else{
$("tr[name='oAssets']").each(function(){
oCheckbox = this.children[0].children[0];
$(this).toggleClass('selected',false);
oCheckbox.checked=0;
});
document.getElementById('check_all').checked=0;
bCheck = 1;
};
};
</script>
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap %}
{% load i18n %}
{% block form %}
<div style="display:none" id="ridd">
{{ form.port|bootstrap_horizontal }}
{{ form.type|bootstrap_horizontal }}
{{ form.idc|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
{{ form.admin_user|bootstrap_horizontal }}
{{ form.system_users|bootstrap_horizontal }}
{{ form.brand|bootstrap_horizontal }}
{{ form.cpu|bootstrap_horizontal }}
{{ form.memory|bootstrap_horizontal }}
{{ form.disk|bootstrap_horizontal }}
{{ form.os|bootstrap_horizontal }}
{{ form.cabinet_no|bootstrap_horizontal }}
{{ form.cabinet_pos|bootstrap_horizontal }}
{{ form.status|bootstrap_horizontal }}
{{ form.env|bootstrap_horizontal }}
{{ form.tags|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
</div>
<div class="hr-line-dashed"></div>
<form action="" class="form-horizontal">
<div class="form-group">
<label class="control-label col-sm-2 col-lg-2 " id="asset_on_count">已选主机({{ assets_count }})</label>
<div class="col-sm-9" id="asset_sed">
<div class="form-asset-on" id="add_asset">
{% for asset in assets_on_list %}
<input type='hidden' name='assets' value='{{ asset.id }}'>
{% endfor %}
<p id="asset_on_p">
{% for asset in assets_on_list %}
<button name='asset_hostname' title='{{ asset.ip }}' type='button' class='btn btn-default btn-xs'>{{ asset.hostname }}</button>
{% endfor %}
</p>
</div>
</div>
</div>
</form>
<div class="hr-line-dashed"></div>
<div class="ydxbd" id="formlists" style="display: block;">
<p id="tags_p" class="mgl-5 c02">选择需要修改属性</p>
<div class="tagBtnList">
<a onclick="AddAllForm(this)" class="tagBtn2 label label-primary" id="changeall">全选</a>
<a onclick="AddForm(this,'id_port')" class="tagBtn2 label label-default" name="changebtn">端口</a>
<a onclick="AddForm(this,'id_type')" class="tagBtn2 label label-default" name="changebtn">系统类型</a>
<a onclick="AddForm(this,'id_idc')" class="tagBtn2 label label-default" name="changebtn">机房</a>
<a onclick="AddForm(this,'id_groups')" class="tagBtn2 label label-default" name="changebtn">用户组</a>
<a onclick="AddForm(this,'id_admin_user')" class="tagBtn2 label label-default" name="changebtn">管理用户</a>
<a onclick="AddForm(this,'id_system_users')" class="tagBtn2 label label-default" name="changebtn">系统用户</a>
<a onclick="AddForm(this,'id_brand')" class="tagBtn2 label label-default" name="changebtn">品牌</a>
<a onclick="AddForm(this,'id_cpu')" class="tagBtn2 label label-default" name="changebtn">CPU</a>
<a onclick="AddForm(this,'id_memory')" class="tagBtn2 label label-default" name="changebtn">内存</a>
<a onclick="AddForm(this,'id_disk')" class="tagBtn2 label label-default" name="changebtn">硬盘</a>
<a onclick="AddForm(this,'id_os')" class="tagBtn2 label label-default" name="changebtn">操作系统</a>
<a onclick="AddForm(this,'id_cabinet_no')" class="tagBtn2 label label-default" name="changebtn">机柜编号</a>
<a onclick="AddForm(this,'id_cabinet_pos')" class="tagBtn2 label label-default" name="changebtn">机柜层号</a>
<a onclick="AddForm(this,'id_status')" class="tagBtn2 label label-default" name="changebtn">资产状态</a>
<a onclick="AddForm(this,'id_env')" class="tagBtn2 label label-default" name="changebtn">资产环境</a>
<a onclick="AddForm(this,'id_tags')" class="tagBtn2 label label-default" name="changebtn">标签</a>
<a onclick="AddForm(this,'id_comment')" class="tagBtn2 label label-default" name="changebtn">备注</a>
</div>
</div>
<input name="assets_ids" type="hidden" value="111" >
<input name="assets_ids" type="hidden" value="112" >
<div class="hr-line-dashed"></div>
<form action="/assets/asset/27/update" method="post" class="form-horizontal" id="add_form">
{% csrf_token %}
<input name="ip" required="" type="hidden" value="1.0.0.0" >
<div class="form-group" name="formbtn" id="formbtn">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="button" onclick="fsubmit()">{% trans 'Submit' %}</button>
</div>
</div>
</form>
<!-- 模态框(Modal) -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" id="box">
<!--此部分为主体内容,将远程加载进来-->
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$('#add_asset').on('click',function(){
$('#modal').modal('show');
});
$('#modal').modal({
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?plain_id_lists={{ plain_id_lists }}",
});
$('#modal').on('show.bs.modal',function(){
//alert('当调用show方法时,立即触发;')
});
$('#modal').on('shown.bs.modal',function(){
//alert('当弹窗完全加载完后,再触发;')
});
$('#modal').on('hide.bs.modal',function(){
//alert('当关闭时,立即触发;')
});
$('#modal').on('hidden.bs.modal',function(){
//alert('当关完全关闭后,再触发;')
});
$('#modal').on('loaded.bs.modal',function(){
//alert('当远程数据加载完毕后,再触发;')
});
function SetSelect2(){
$('.select2').select2();
$("#id_tags").select2({
tags: true,
maximumSelectionLength: 8, //最多能够选择的个数
//closeOnSelect: false
});
};
function AddForm(obj,id_form) {
var oHiddenForms = document.getElementById("ridd");
var parentElem = document.getElementById("add_form");
var oH = document.getElementById(id_form);
var oNew = oH.parentNode.parentNode
var aDiv = parentElem.getElementsByClassName('form-group');
if(oNew.parentNode.id=='ridd') {
obj.className="tagBtn2 label label-warning";
parentElem.insertBefore(oNew,aDiv[0]);
SetSelect2();
}else{
oHiddenForms.appendChild(oNew);
obj.className="tagBtn2 label label-default";
SetSelect2();
};
};
function ChangeBtnCss(class_var){
var changebtns = $("#formlists").find("a[name='changebtn']")
for (var i=0; i<changebtns.length;i++){
changebtns[i].className=class_var;
};
};
function AddAllForm(obj) {
var oHiddenForms = document.getElementById("ridd");
var parentElem = document.getElementById("add_form");
var aDiv = parentElem.getElementsByClassName('form-group');
var bFormBtn = document.getElementById("formbtn");
var oHidden_len = oHiddenForms.children.length;
var aDiv_len = aDiv.length;
if(oHidden_len == 0 || obj.innerText == "取消全选"){
for(var i=0;i<aDiv_len-1;i++){
oHiddenForms.appendChild(aDiv[0]);
};
ChangeBtnCss("tagBtn2 label label-default");
$('#changeall').text("全选");
}else{
for(var i=0;i<oHidden_len;i++){
parentElem.insertBefore(oHiddenForms.children[0],bFormBtn);
};
ChangeBtnCss("tagBtn2 label label-warning");
$('#changeall').text("取消全选");
SetSelect2();
};
};
function fsubmit(){
var assets_id = document.getElementsByName("assets");
var oForm = document.getElementById('add_form');
var parentElem = document.getElementById("add_form");
var aDiv = parentElem.getElementsByClassName('form-group');
if (assets_id.length === 0) {
swal({
title: "未选择需要修改的主机",
text: "请点击选择"
});
}else if (aDiv.length === 1) {
swal({
title: "未选需要修改的属性",
text: "请点击选择"
});
}else{
var m = document.getElementsByName('assets_ids');
alert(m.length);
for(var i=0;i<m.length;i++){
alert(m[0].value);
oForm.appendChild(m[0]);
};
action="/assets/asset/"+assets_id[0].value+"/update";
oForm.action=action;
oForm.submit();
};
}
</script>
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style>
div.dataTables_wrapper div.dataTables_filter,
.dataTables_length {
float: left;
}
</style>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> {{ action }}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<div class="panel blank-panel">
<div class="panel-body">
<div class="tab-content">
<div id="tab-1" class="ibox float-e-margins tab-pane active"></div>
<form id="tagForm" method="post" class="form-horizontal">
{% csrf_token %}
<h3 class="widget-head-color-box">基本信息</h3>
{{ form.name|bootstrap_horizontal }}
<div class="form-group">
<label class="col-sm-2 control-label" id="asset_on_count">关联的资产({{ assets_count }})</label>
<div class="col-sm-9" id="asset_sed">
<div class="form-asset-on" id="add_asset">
<p id="asset_on_p">
{% for asset in assets_on_list %}
<button name='asset_hostname' title='{{ asset.ip}}' type='button' class='btn btn-default btn-xs'>{{ asset.hostname }}</button>
{% endfor %}
</p>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-5">
<button class="btn btn-white" type="reset"> 重置 </button>
<button class="btn btn-primary" type="submit"> 提交 </button>
<div id='box2'> </div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 模态框(Modal) -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" id="box">
<!--此部分为主体内容,将远程加载进来-->
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
})
$('#add_asset').on('click',function(){
$('#modal').modal('show');
});
$('#modal').modal({
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?tag_id={{ tag_id }}",
});
$('#modal').on('show.bs.modal',function(){
//alert('当调用show方法时,立即触发;')
});
$('#modal').on('shown.bs.modal',function(){
//alert('当弹窗完全加载完后,再触发;')
});
$('#modal').on('hide.bs.modal',function(){
//alert('当关闭时,立即触发;')
});
$('#modal').on('hidden.bs.modal',function(){
//alert('当关完全关闭后,再触发;')
});
$('#modal').on('loaded.bs.modal',function(){
//alert('当远程数据加载完毕后,再触发;')
});
</script>
{% endblock %}
\ No newline at end of file
This diff is collapsed.
{% extends '_base_list.html' %}
{% load i18n static %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{#{% block content_left_head %}#}
{# <a href="{% url 'assets:asset-tag-create' %}" class="btn btn-sm btn-primary "> {% trans "Create tag" %}</a>#}
{#{% endblock %}#}
{#{% block table_head %}#}
{# <th class="text-center">#}
{# <input type="checkbox" id="check_all" onclick="checkAll('check_all', 'checked')">#}
{# </th>#}
{# <th class="text-center"><a href="{% url 'assets:asset-tag-list' %}?sort=name">{% trans 'Tag Name' %}</a></th>#}
{# <th class="text-center">{% trans 'Asset num' %}</th>#}
{# <th class="text-center"></th>#}
{#{% endblock %}#}
{#{% block table_body %}#}
{# {% for asset_tag in asset_tags_list %}#}
{# <tr class="gradeX">#}
{# <td class="text-center">#}
{# <input type="checkbox" name="checked" value="{{ asset_tag.id }}">#}
{# </td>#}
{# <td class="text-center">#}
{# <a href="{% url 'assets:asset-tag-detail' pk=asset_tag.id %}">#}
{# {{ asset_tag.name }}#}
{# </a>#}
{# </td>#}
{# <td class="text-center">{{ asset_tag.asset_set.count }}</td>#}
{# <td class="text-center">#}
{# <a href="{% url 'assets:asset-tag-update' pk=asset_tag.id %}" class="btn btn-xs btn-info">{% trans 'Update' %}</a>#}
{# <a onclick="objectDelete(this,'{{ asset_tag.name }}','{% url 'assets:asset-tag-delete' asset_tag.id %}')" class="btn btn-xs btn-danger del">{% trans 'Delete' %}</a>#}
{# </td>#}
{# </tr>#}
{# {% endfor %}#}
{#{% endblock %}#}
{#{% block content_bottom_left %}#}
{# <form id="" method="get" action="" class=" mail-search">#}
{# <div class="input-group">#}
{# <select class="form-control m-b" style="width: auto">#}
{# <option>{% trans 'Delete selected' %}</option>#}
{# <option>{% trans 'Update selected' %}</option>#}
{# <option>{% trans 'Deactive selected' %}</option>#}
{# <option>{% trans 'Export selected' %}</option>#}
{# </select>#}
{# <div class="input-group-btn pull-left" style="padding-left: 5px;">#}
{# <button id='search_btn' type="submit" style="height: 32px;" class="btn btn-sm btn-primary">#}
{# {% trans 'Submit' %}#}
{# </button>#}
{# </div>#}
{# </div>#}
{# </form>#}
{#{% endblock %}#}
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5">
<a href="{% url "assets:asset-tag-create" %}" class="btn btn-sm btn-primary"> {% trans "Create Tag" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="tag_list_table" >
<thead>
<tr>
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<th class="text-center">{% trans 'TagName' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update 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/jquery.form.min.js' %}"></script>
<script type="text/javascript">
$(document).ready(function () {
var options = {
ele: $("#tag_list_table"),
columnDefs:[
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url 'assets:asset-tag-detail' pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url 'assets:asset-tag-detail' pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_tag_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:asset-tag-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" },{data: "id"}]
};
jumpserver.initDataTable(options);
})
.on('click', '.btn_tag_delete', function () {
var $this = $(this);
var $data_table = $('#tag_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:asset-tag-detail" pk=99991937 %}'.replace('99991937', uid);
objectDelete($this, name, the_url);
$data_table.ajax.reload();
})
</script>
{% endblock %}
This diff is collapsed.
{% load i18n %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% trans 'Confirm delete' %}</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
<p>{% trans 'Are you sure delete' %} <b>{{ object.name }} </b> ?</p>
<input type="submit" value="Confirm" />
</form>
</body>
</html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
from django import template
from django.utils import timezone
from django.conf import settings
register = template.Library()
from django.test import TestCase
# Create your tests here.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
from __future__ import unicode_literals
from django.apps import AppConfig
class AuditsConfig(AppConfig):
name = 'audits'
from importlib import import_module
from django.conf import settings
command_engine = import_module(settings.COMMAND_STORE_BACKEND)
command_store = command_engine.CommandStore()
record_engine = import_module(settings.RECORD_STORE_BACKEND)
record_store = record_engine.RecordStore()
from .command.serializers import CommandLogSerializer
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# ~*~ coding: utf-8 ~*~
#
from users.utils import AdminUserRequiredMixin
from users.models import User
from assets.models import Asset, SystemUser
from users.permissions import IsSuperUserOrAppUser, IsAppUser
from applications.models import Terminal
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
from django.test import TestCase
# Create your tests here.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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