mirror of https://github.com/jumpserver/jumpserver
merge with version 0.4.0
commit
78700cd2f5
|
@ -1,46 +1,23 @@
|
||||||
*.py[cod]
|
|
||||||
.idea
|
|
||||||
test.py
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
db.sqlite3
|
*.pyc
|
||||||
# C extensions
|
*.pyo
|
||||||
*.so
|
*.swp
|
||||||
|
.env
|
||||||
# Packages
|
env
|
||||||
*.egg
|
env*
|
||||||
*.egg-info
|
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
eggs
|
*.egg
|
||||||
parts
|
*.egg-info
|
||||||
bin
|
_mailinglist
|
||||||
var
|
dump.rdb
|
||||||
sdist
|
|
||||||
develop-eggs
|
|
||||||
.installed.cfg
|
|
||||||
lib
|
|
||||||
lib64
|
|
||||||
__pycache__
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
.coverage
|
|
||||||
.tox
|
.tox
|
||||||
nosetests.xml
|
.cache/
|
||||||
|
.idea/
|
||||||
# Translations
|
db.sqlite3
|
||||||
*.mo
|
config.py
|
||||||
|
migrations/
|
||||||
# Mr Developer
|
|
||||||
.mr.developer.cfg
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
.settings
|
|
||||||
*.log
|
*.log
|
||||||
logs/*
|
host_rsa_key
|
||||||
keys/*
|
*.bat
|
||||||
jumpserver.conf
|
tags
|
||||||
nohup.out
|
|
||||||
tmp/*
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
system
|
|
@ -0,0 +1,13 @@
|
||||||
|
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
|
|
@ -0,0 +1,9 @@
|
||||||
|
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
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
|
@ -0,0 +1,109 @@
|
||||||
|
# -*- 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'})
|
|
@ -0,0 +1,7 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationsConfig(AppConfig):
|
||||||
|
name = 'applications'
|
|
@ -0,0 +1,19 @@
|
||||||
|
# ~*~ 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'})
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from users.models import User
|
||||||
|
from users.permissions import IsSuperUserOrAppUser, IsAppUser
|
||||||
|
from audits.models import ProxyLog
|
|
@ -0,0 +1,62 @@
|
||||||
|
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'
|
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- 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
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,5 @@
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<input type="submit" value="Submit">
|
||||||
|
</form>
|
|
@ -0,0 +1,72 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/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'),
|
||||||
|
]
|
|
@ -0,0 +1,90 @@
|
||||||
|
# ~*~ 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)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
# ~*~ 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,)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AssetsConfig(AppConfig):
|
||||||
|
name = 'assets'
|
|
@ -0,0 +1,307 @@
|
||||||
|
# coding:utf-8
|
||||||
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser, Tag
|
||||||
|
from common.utils import validate_ssh_private_key, ssh_pubkey_gen
|
||||||
|
|
||||||
|
|
||||||
|
class AssetCreateForm(forms.ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
instance = kwargs.get('instance', None)
|
||||||
|
if instance:
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
|
initial['tags'] = [t.pk for t in kwargs['instance'].tags.all()]
|
||||||
|
super(AssetCreateForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _save_m2m(self):
|
||||||
|
super(AssetCreateForm, self)._save_m2m()
|
||||||
|
tags = self.cleaned_data['tags']
|
||||||
|
self.instance.tags.clear()
|
||||||
|
self.instance.tags.add(*tuple(tags))
|
||||||
|
|
||||||
|
# def clean(self):
|
||||||
|
# clean_data = super(AssetCreateForm, self).clean()
|
||||||
|
# ip = clean_data.get('ip')
|
||||||
|
# port = clean_data.get('port')
|
||||||
|
# query = Asset.objects.filter(ip=ip, port=port)
|
||||||
|
# if query:
|
||||||
|
# raise forms.ValidationError('this asset has exists.')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Asset
|
||||||
|
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all())
|
||||||
|
fields = [
|
||||||
|
'hostname', 'ip', 'port', 'type', 'comment', 'admin_user', 'system_users', 'idc', 'groups',
|
||||||
|
'other_ip', 'remote_card_ip', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no',
|
||||||
|
'cabinet_pos', 'number', 'status', 'env', 'sn', 'tags',
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'groups': forms.SelectMultiple(attrs={'class': 'select2',
|
||||||
|
'data-placeholder': _('Select asset groups')}),
|
||||||
|
'tags': forms.SelectMultiple(attrs={'class': 'select2',
|
||||||
|
'data-placeholder': _('Select asset tags')}),
|
||||||
|
'system_users': forms.SelectMultiple(attrs={'class': 'select2',
|
||||||
|
'data-placeholder': _('Select asset system users')}),
|
||||||
|
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}),
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'hostname': '* required',
|
||||||
|
'ip': '* required',
|
||||||
|
'system_users': _('System user will be granted for user to login assets (using ansible create automatic)'),
|
||||||
|
'admin_user': _('Admin user should be exist on asset already, And have sudo ALL permission'),
|
||||||
|
'tags': '最多5个标签,单个标签最长8个汉字,按回车确认'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupForm(forms.ModelForm):
|
||||||
|
# See AdminUserForm comment same it
|
||||||
|
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||||
|
label=_('Asset'),
|
||||||
|
required=False,
|
||||||
|
widget=forms.SelectMultiple(
|
||||||
|
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if kwargs.get('instance', None):
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
|
initial['assets'] = kwargs['instance'].assets.all()
|
||||||
|
super(AssetGroupForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _save_m2m(self):
|
||||||
|
super(AssetGroupForm, self)._save_m2m()
|
||||||
|
assets = self.cleaned_data['assets']
|
||||||
|
self.instance.assets.clear()
|
||||||
|
self.instance.assets.add(*tuple(assets))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AssetGroup
|
||||||
|
fields = [
|
||||||
|
"name", "comment","system_users",
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'name' : forms.TextInput(attrs={}),
|
||||||
|
'system_users': forms.SelectMultiple(attrs={'class': 'select2-system-user', 'data-placeholder': _('Select asset system user')}),
|
||||||
|
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'name': '* required',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class IDCForm(forms.ModelForm):
|
||||||
|
# See AdminUserForm comment same it
|
||||||
|
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||||
|
label=_('Asset'),
|
||||||
|
required=False,
|
||||||
|
widget=forms.SelectMultiple(
|
||||||
|
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if kwargs.get('instance'):
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
|
initial['assets'] = kwargs['instance'].assets.all()
|
||||||
|
super(IDCForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _save_m2m(self):
|
||||||
|
super(IDCForm, self)._save_m2m()
|
||||||
|
assets = self.cleaned_data['assets']
|
||||||
|
self.instance.assets.clear()
|
||||||
|
self.instance.assets.add(*tuple(assets))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = IDC
|
||||||
|
fields = ['name', "bandwidth", "operator", 'contact', 'phone', 'address', 'intranet', 'extranet','comment']
|
||||||
|
widgets = {
|
||||||
|
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
||||||
|
'intranet': forms.Textarea(
|
||||||
|
attrs={'placeholder': 'IP段之间用逗号隔开,如:192.168.1.0/24,192.168.1.0/24'}),
|
||||||
|
'extranet': forms.Textarea(
|
||||||
|
attrs={'placeholder': 'IP段之间用逗号隔开,如:201.1.32.1/24,202.2.32.1/24'})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserForm(forms.ModelForm):
|
||||||
|
# Admin user assets define, let user select, save it in form not in view
|
||||||
|
assets = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=Asset.objects.all(),
|
||||||
|
label=_('Asset'),
|
||||||
|
required=False,
|
||||||
|
widget=forms.SelectMultiple(
|
||||||
|
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
|
||||||
|
)
|
||||||
|
# Form field name can not start with `_`, so redefine it,
|
||||||
|
password = forms.CharField(
|
||||||
|
widget=forms.PasswordInput, max_length=100,
|
||||||
|
min_length=8, strip=True, required=False,
|
||||||
|
help_text=_('If also set private key, use that first'),
|
||||||
|
)
|
||||||
|
# Need use upload private key file except paste private key content
|
||||||
|
private_key_file = forms.FileField(required=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# When update a admin user instance, initial it
|
||||||
|
if kwargs.get('instance'):
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
|
initial['assets'] = kwargs['instance'].assets.all()
|
||||||
|
super(AdminUserForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _save_m2m(self):
|
||||||
|
# Save assets relation with admin user
|
||||||
|
super(AdminUserForm, self)._save_m2m()
|
||||||
|
assets = self.cleaned_data['assets']
|
||||||
|
self.instance.assets.clear()
|
||||||
|
self.instance.assets.add(*tuple(assets))
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
# Because we define custom field, so we need rewrite :method: `save`
|
||||||
|
admin_user = super(AdminUserForm, self).save(commit=commit)
|
||||||
|
password = self.cleaned_data['password']
|
||||||
|
private_key = self.cleaned_data['private_key_file']
|
||||||
|
|
||||||
|
if password:
|
||||||
|
admin_user.password = password
|
||||||
|
if private_key:
|
||||||
|
public_key = ssh_pubkey_gen(private_key)
|
||||||
|
admin_user.private_key = private_key
|
||||||
|
admin_user.public_key = public_key
|
||||||
|
admin_user.save()
|
||||||
|
return admin_user
|
||||||
|
|
||||||
|
def clean_private_key_file(self):
|
||||||
|
private_key_file = self.cleaned_data['private_key_file']
|
||||||
|
if private_key_file:
|
||||||
|
private_key = private_key_file.read()
|
||||||
|
if not validate_ssh_private_key(private_key):
|
||||||
|
raise forms.ValidationError(_('Invalid private key'))
|
||||||
|
return private_key
|
||||||
|
return private_key_file
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
password = self.cleaned_data['password']
|
||||||
|
private_key_file = self.cleaned_data.get('private_key_file', '')
|
||||||
|
|
||||||
|
if not self.instance and not (password or private_key_file):
|
||||||
|
raise forms.ValidationError(
|
||||||
|
_('Password and private key file must be input one'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = AdminUser
|
||||||
|
fields = ['name', 'username', 'password', 'private_key_file', 'comment']
|
||||||
|
widgets = {
|
||||||
|
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
||||||
|
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'name': '* required',
|
||||||
|
'username': '* required',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserForm(forms.ModelForm):
|
||||||
|
# Admin user assets define, let user select, save it in form not in view
|
||||||
|
auto_generate_key = forms.BooleanField(initial=True, required=False)
|
||||||
|
# Form field name can not start with `_`, so redefine it,
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True)
|
||||||
|
# Need use upload private key file except paste private key content
|
||||||
|
private_key_file = forms.FileField(required=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# When update a admin user instance, initial it
|
||||||
|
if kwargs.get('instance'):
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
|
initial['assets'] = kwargs['instance'].assets.all()
|
||||||
|
initial['asset_groups'] = kwargs['instance'].asset_groups.all()
|
||||||
|
super(SystemUserForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
# Because we define custom field, so we need rewrite :method: `save`
|
||||||
|
system_user = super(SystemUserForm, self).save(commit=commit)
|
||||||
|
password = self.cleaned_data['password']
|
||||||
|
private_key_file = self.cleaned_data['private_key_file']
|
||||||
|
|
||||||
|
if system_user.auth_method == 'P':
|
||||||
|
if password:
|
||||||
|
system_user.password = password
|
||||||
|
print(password)
|
||||||
|
# Todo: Validate private key file, and generate public key
|
||||||
|
# Todo: Auto generate private key and public key
|
||||||
|
if private_key_file:
|
||||||
|
system_user.private_key = private_key_file.read().strip()
|
||||||
|
system_user.save()
|
||||||
|
return self.instance
|
||||||
|
|
||||||
|
# Todo: check valid
|
||||||
|
# def clean_private_key_file(self):
|
||||||
|
# if not self.cleaned_data['auto_generate_key']:
|
||||||
|
# if not self.cleaned_data['private_key_file']:
|
||||||
|
# raise forms.ValidationError(_('Private key required'))
|
||||||
|
|
||||||
|
# def clean_password(self):
|
||||||
|
# if self.cleaned_data['auth_method'] == 'P':
|
||||||
|
# if not self.cleaned_data['password']:
|
||||||
|
# raise forms.ValidationError(_('Password required'))
|
||||||
|
# return self.cleaned_data['password']
|
||||||
|
|
||||||
|
# def clean(self):
|
||||||
|
# password = self.cleaned_data['password']
|
||||||
|
# private_key_file = self.cleaned_data.get('private_key_file', '')
|
||||||
|
#
|
||||||
|
# if not (password or private_key_file):
|
||||||
|
# raise forms.ValidationError(_('Password and private key file must be input one'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SystemUser
|
||||||
|
fields = [
|
||||||
|
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'auth_method',
|
||||||
|
'auto_push', 'sudo', 'comment', 'shell', 'home', 'uid',
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
|
||||||
|
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'name': '* required',
|
||||||
|
'username': '* required',
|
||||||
|
'auto_push': 'Auto push system user to asset',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AssetTagForm(forms.ModelForm):
|
||||||
|
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
|
||||||
|
label=_('Asset'),
|
||||||
|
required=False,
|
||||||
|
widget=forms.SelectMultiple(
|
||||||
|
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
if kwargs.get('instance', None):
|
||||||
|
initial = kwargs.get('initial', {})
|
||||||
|
initial['assets'] = kwargs['instance'].asset_set.all()
|
||||||
|
super(AssetTagForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def _save_m2m(self):
|
||||||
|
assets = self.cleaned_data['assets']
|
||||||
|
self.instance.assets.clear()
|
||||||
|
self.instance.assets.add(*tuple(assets))
|
||||||
|
super(AssetTagForm, self)._save_m2m()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Tag
|
||||||
|
fields = [
|
||||||
|
"name",
|
||||||
|
]
|
||||||
|
widgets = {
|
||||||
|
'name' : forms.TextInput(attrs={}),
|
||||||
|
|
||||||
|
}
|
||||||
|
help_texts = {
|
||||||
|
'name': '* required',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FileForm(forms.Form):
|
||||||
|
file = forms.FileField()
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""
|
||||||
|
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
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from .idc import *
|
||||||
|
from .user import *
|
||||||
|
from .group import *
|
||||||
|
from .asset import *
|
|
@ -0,0 +1,128 @@
|
||||||
|
#!/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'
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/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
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
#!/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
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,198 @@
|
||||||
|
# -*- 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']
|
|
@ -0,0 +1,82 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,63 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,440 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>Update</a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-danger btn-delete-admin-user">
|
||||||
|
<i class="fa fa-edit"></i>Delete
|
||||||
|
</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>{{ admin_user.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>{% trans 'Name' %}:</td>
|
||||||
|
<td><b>{{ admin_user.name }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Username' %}:</td>
|
||||||
|
<td><b>{{ admin_user.username }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date created' %}:</td>
|
||||||
|
<td><b>{{ admin_user.date_created }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ asset_group.created_by }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Comment' %}:</td>
|
||||||
|
<td><b>{{ admin_user.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.name }}</b> <span class="badge"> {{ paginator.count }}</span></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 table-hover" id="system_user_assets_table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Hostname' %}</th>
|
||||||
|
<th>{% trans 'IP' %}</th>
|
||||||
|
<th>{% trans 'Port' %}</th>
|
||||||
|
<th>{% trans 'Alive' %}</th>
|
||||||
|
<th>{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{# {% for asset in page_obj %}#}
|
||||||
|
{# <tr>#}
|
||||||
|
{# <td>{{ asset.hostname }}</td>#}
|
||||||
|
{# <td>{{ asset.ip }}</td>#}
|
||||||
|
{# <td>{{ asset.port }}</td>#}
|
||||||
|
{# <td>Alive</td>#}
|
||||||
|
{# </tr>#}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{# <div class="row">#}
|
||||||
|
{# {% include '_pagination.html' %}#}
|
||||||
|
{# </div>#}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td width="50%">{% trans 'Get install script' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span style="float: right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Get' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span style="float: right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="50%">{% trans 'Reset private key' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span style="float: right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Replace asset admin user with this' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset in assets_remain %}
|
||||||
|
<option value="{{ asset.id }}">{{ asset.ip }}:{{ asset.port }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-info btn-sm btn-replace-asset-admin_user">{% trans 'Replace' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-warning">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Replace asset admin user with this admin user' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select asset groups' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-warning btn-sm btn-replace-asset_groups-admin_user">{% trans 'Replace' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
Array.prototype.remove = function(val) {
|
||||||
|
var index = this.indexOf(val);
|
||||||
|
if (index > -1) {
|
||||||
|
this.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Array.prototype.unique = function(){
|
||||||
|
var res = [];
|
||||||
|
var json = {};
|
||||||
|
for(var i = 0; i < this.length; i++){
|
||||||
|
if(!json[this[i]]){
|
||||||
|
res.push(this[i]);
|
||||||
|
json[this[i]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
function objectRemove(obj, name, url, data) {
|
||||||
|
function doRemove() {
|
||||||
|
var body = data;
|
||||||
|
var success = function() {
|
||||||
|
swal('Remove!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
$(obj).parent().parent().remove();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Remove"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure remove ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doRemove()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
jumpserver.asset_groups_selected = {};
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2()
|
||||||
|
.on("select2:select", function (evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
jumpserver.assets_selected[data.id] = data.text;
|
||||||
|
jumpserver.asset_groups_selected[data.id] = data.text;
|
||||||
|
})
|
||||||
|
.on('select2:unselect', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
delete jumpserver.assets_selected[data.id];
|
||||||
|
delete jumpserver.asset_groups_selected[data.id]
|
||||||
|
});
|
||||||
|
var options = {
|
||||||
|
ele: $('#system_user_assets_table'),
|
||||||
|
buttons: [],
|
||||||
|
order: [],
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 0, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}" data-aid="'+rowData.id+'">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 3, 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: 4, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:asset-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_remove" data-aid="99991937">{% trans "Remove" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:asset-list" %}?admin_user_id={{ admin_user.id }}',
|
||||||
|
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||||
|
{data: "is_active" }, {data: "id"}],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
|
||||||
|
function adminUserDelete(name, url) {
|
||||||
|
function doDelete() {
|
||||||
|
var body = {};
|
||||||
|
var success = function() {
|
||||||
|
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
window.location.href="{% url 'assets:idc-list' %}";
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'DELETE',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure delete ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-replace-asset-admin_user', function () {
|
||||||
|
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jumpserver.asset_groups_selected = {};
|
||||||
|
var $data_table = $("#system_user_assets_table").DataTable();
|
||||||
|
var assets = [];
|
||||||
|
$.map(jumpserver.assets_selected, function(value, index) {
|
||||||
|
assets.push(parseInt(index));
|
||||||
|
});
|
||||||
|
assets.unique();
|
||||||
|
var data = [];
|
||||||
|
var admin_user_id = {{ admin_user.id }};
|
||||||
|
var the_url = '{% url "api-assets:asset-list" %}';
|
||||||
|
for (var i=0; i<assets.length; i++) {
|
||||||
|
data.push({"id": assets[i], "admin_user": admin_user_id});
|
||||||
|
}
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
method: 'PATCH'
|
||||||
|
});
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-replace-asset_groups-admin_user', function () {
|
||||||
|
if (Object.keys(jumpserver.asset_groups_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
var $data_table = $("#system_user_assets_table").DataTable();
|
||||||
|
var asset_groups = [];
|
||||||
|
var assets = [];
|
||||||
|
var data = [];
|
||||||
|
var the_url = '{% url "api-assets:asset-list" %}';
|
||||||
|
$.map(jumpserver.asset_groups_selected, function(value, index) {
|
||||||
|
asset_groups.push(parseInt(index));
|
||||||
|
});
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:asset-group-list" %}?id__in=['+asset_groups.join(',')+']',
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for (var i=0; i<result.length; i++) {
|
||||||
|
for (var j=0; j<result[i]['assets'].length; j++) {
|
||||||
|
assets.push(result[i]['assets'][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var z=0; z<assets.length; z++) {
|
||||||
|
data.push({"id":assets[z], "admin_user":{{admin_user.id}} });
|
||||||
|
}
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
method: 'PATCH'
|
||||||
|
});
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_asset_remove', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var the_url = "{% url 'api-assets:admin-user-detail' pk=admin_user.id %}";
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(1) > a").html();
|
||||||
|
var assets = [];
|
||||||
|
var delete_asset_id = $(this).data('aid');
|
||||||
|
$.ajax({
|
||||||
|
url: the_url,
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for (var i=0; i<result['assets'].length; i++) {
|
||||||
|
assets.push(result['assets'][i])
|
||||||
|
}
|
||||||
|
assets.remove(delete_asset_id);
|
||||||
|
var data = {"assets": assets};
|
||||||
|
objectRemove($this, name, the_url, data);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).on('click', '.btn-delete-admin-user', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var name = "{{ admin_user.name }}";
|
||||||
|
var uid = "{{ admin_user.id }}";
|
||||||
|
var the_url = '{% url "api-assets:admin-user-detail" pk=99991937 %}'.replace('99991937', uid);
|
||||||
|
var redirect_url = "{% url 'assets:admin-user-list' %}";
|
||||||
|
objectDelete($this, name, the_url, redirect_url);
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,137 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,426 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block custom_head_css_js %}
|
||||||
|
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
|
||||||
|
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
|
||||||
|
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
|
||||||
|
<script src="{% static "js/plugins/sweetalert/sweetalert.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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Asset detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.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>{{ asset.hostname }}</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 'Hostname' %}:</td>
|
||||||
|
<td><b>{{ asset.hostname }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'IP' %}:</td>
|
||||||
|
<td><b>{{ asset.ip }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Admin user' %}:</td>
|
||||||
|
{% if asset.admin_user %}
|
||||||
|
<td><b>{{ asset.admin_user.name }}</b></td>
|
||||||
|
{% else %}
|
||||||
|
<td><b>None</b></td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Other IP' %}:</td>
|
||||||
|
<td><b>{{ asset.other_ip }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Remote card IP' %}:</td>
|
||||||
|
<td><b>{{ asset.remote_card_ip }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Port' %}:</td>
|
||||||
|
<td><b>{{ asset.port }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Mac address' %}:</td>
|
||||||
|
<td><b>{{ asset.mac_addr }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'CPU' %}:</td>
|
||||||
|
<td><b>{{ asset.cpu }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Memory' %}:</td>
|
||||||
|
<td><b>{{ asset.memory }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Disk' %}:</td>
|
||||||
|
<td><b>{{ asset.disk }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'OS' %}:</td>
|
||||||
|
<td><b>{{ asset.os }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Asset status' %}:</td>
|
||||||
|
<td><b>{{ asset.status }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Is active' %}:</td>
|
||||||
|
<td><b>{{ asset.is_active }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Asset type' %}:</td>
|
||||||
|
<td><b>{{ asset.type }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Asset environment' %}:</td>
|
||||||
|
<td><b>{{ asset.env }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Serial number' %}:</td>
|
||||||
|
<td><b>{{ asset.sn }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Asset number' %}:</td>
|
||||||
|
<td><b>{{ asset.number }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ asset.created_by }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date joined' %}:</td>
|
||||||
|
<td><b>{{ asset.date_joined|date:"Y-m-j H:i:s" }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Comment' %}:</td>
|
||||||
|
<td><b>{{ asset.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Quick modify' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td width="50%">{% trans 'Active' %}:</td>
|
||||||
|
<td><span class="pull-right">
|
||||||
|
<div class="switch">
|
||||||
|
<div class="onoffswitch">
|
||||||
|
<input type="checkbox" {% if asset.is_active %} checked {% endif %} class="onoffswitch-checkbox" id="is_active">
|
||||||
|
<label class="onoffswitch-label" for="is_active">
|
||||||
|
<span class="onoffswitch-inner"></span>
|
||||||
|
<span class="onoffswitch-switch"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Rrefresh hardware' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span class="pull-right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Refresh' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Test admin user' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span class="pull-right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Test' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Test system users' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span class="pull-right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Test' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Repush system users' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span class="pull-right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_pk" style="width: 54px;">{% trans 'Push' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Asset groups' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table group_edit" id="add-asset2group">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<select data-placeholder="{% trans 'Join asset groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset_group in asset_groups_remain %}
|
||||||
|
<option value="{{ asset_group.id }}" id="opt_{{ asset_group.id }}" >{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<button type="button" class="btn btn-info btn-sm" id="btn_add_user_group">{% trans 'Join' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<tr>
|
||||||
|
<td ><b class="bdg_group" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-warning">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'System users' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table group_edit" id="add-asset2systemuser">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select system user' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for system_user in system_users_remain %}
|
||||||
|
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-warning btn-sm btn-system-user">{% trans 'Associate' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
{% for system_user in system_users %}
|
||||||
|
<tr>
|
||||||
|
<td ><b class="bdg_group" data-sid={{ system_user.id }}>{{ system_user.name }}</b></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger btn-xs pull-right btn_leave_system" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
jumpserver.groups_selected = {};
|
||||||
|
function updateAssetGroups(groups) {
|
||||||
|
var the_url = "{% url 'api-assets:asset-update-group' pk=asset.id %}";
|
||||||
|
var body = {
|
||||||
|
groups: Object.assign([], groups)
|
||||||
|
};
|
||||||
|
var success = function(data) {
|
||||||
|
// remove all the selected groups from select > option and rendered ul element;
|
||||||
|
$('.select2-selection__rendered').empty();
|
||||||
|
$('#groups_selected').val('');
|
||||||
|
$.map(jumpserver.groups_selected, function(group_name, index) {
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
// change tr html of user groups.
|
||||||
|
$('#add-asset2group tbody').append(
|
||||||
|
'<tr>' +
|
||||||
|
'<td><b class="bdg_group" data-gid="' + index + '">' + group_name + '</b></td>' +
|
||||||
|
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||||
|
'</tr>'
|
||||||
|
)
|
||||||
|
});
|
||||||
|
// clear jumpserver.groups_selected
|
||||||
|
jumpserver.groups_selected = {};
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssetSystem(system_users) {
|
||||||
|
var the_url = "{% url 'api-assets:asset-update-system-users' pk=asset.id %}";
|
||||||
|
var body = {
|
||||||
|
system_users: Object.assign([], system_users)
|
||||||
|
};
|
||||||
|
var success = function(data) {
|
||||||
|
$('.select2-selection__rendered').empty();
|
||||||
|
$('#groups_selected').val('');
|
||||||
|
$.map(jumpserver.groups_selected, function(name, index) {
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
|
||||||
|
$('#add-asset2systemuser tbody').append(
|
||||||
|
'<tr>' +
|
||||||
|
'<td><b class="bdg_group" data-sid="' + index + '">' + name + '</b></td>' +
|
||||||
|
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_system" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||||
|
'</tr>'
|
||||||
|
)
|
||||||
|
});
|
||||||
|
// clear jumpserver.groups_selected
|
||||||
|
jumpserver.groups_selected = {};
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2()
|
||||||
|
.on('select2:select', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
jumpserver.groups_selected[data.id] = data.text;
|
||||||
|
}).on('select2:unselect', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
delete jumpserver.groups_selected[data.id]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on('click', '#is_active', function () {
|
||||||
|
var the_url = '{% url "api-assets:asset-detail" pk=asset.id %}';
|
||||||
|
var checked = $(this).prop('checked');
|
||||||
|
var body = {
|
||||||
|
'is_active': checked
|
||||||
|
};
|
||||||
|
var success = '{% trans "Update Successfully!" %}';
|
||||||
|
var status = $(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").text();
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
success_message: success
|
||||||
|
});
|
||||||
|
if (status == "False") {
|
||||||
|
$(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").html('True');
|
||||||
|
}else{
|
||||||
|
$(".ibox-content > table > tbody > tr:nth-child(13) > td:last >b").html('False');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('click', '#btn_add_user_group', function () {
|
||||||
|
if (Object.keys(jumpserver.groups_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var groups = $('.bdg_group').map(function() {
|
||||||
|
return $(this).data('gid');
|
||||||
|
}).get();
|
||||||
|
$.map(jumpserver.groups_selected, function(value, index) {
|
||||||
|
groups.push(parseInt(index));
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
});
|
||||||
|
updateAssetGroups(groups)
|
||||||
|
})
|
||||||
|
.on('click', '.btn_leave_group', function() {
|
||||||
|
var $this = $(this);
|
||||||
|
var $tr = $this.closest('tr');
|
||||||
|
var $badge = $tr.find('.bdg_group');
|
||||||
|
var gid = $badge.data('gid');
|
||||||
|
var group_name = $badge.html() || $badge.text();
|
||||||
|
$('#groups_selected').append(
|
||||||
|
'<option value="' + gid + '" id="opt_' + gid + '">' + group_name + '</option>'
|
||||||
|
);
|
||||||
|
$tr.remove();
|
||||||
|
var groups = $('.bdg_group').map(function () {
|
||||||
|
return $(this).data('gid');
|
||||||
|
}).get();
|
||||||
|
updateAssetGroups(groups)
|
||||||
|
})
|
||||||
|
.on('click', '.btn-system-user', function () {
|
||||||
|
if (Object.keys(jumpserver.groups_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var system_users = $('.bdg_group').map(function() {
|
||||||
|
return $(this).data('sid');
|
||||||
|
}).get();
|
||||||
|
$.map(jumpserver.groups_selected, function(value, index) {
|
||||||
|
system_users.push(parseInt(index));
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
});
|
||||||
|
updateAssetSystem(system_users)
|
||||||
|
|
||||||
|
})
|
||||||
|
.on('click', '.btn_leave_system', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $tr = $this.closest('tr');
|
||||||
|
var $badge = $tr.find('.bdg_group');
|
||||||
|
var sid = $badge.data('sid');
|
||||||
|
var name = $badge.html() || $badge.text();
|
||||||
|
$('#groups_selected').append(
|
||||||
|
'<option value="' + sid + '" id="opt_' + sid + '">' + name + '</option>'
|
||||||
|
);
|
||||||
|
$tr.remove();
|
||||||
|
var system_users = $('.bdg_group').map(function () {
|
||||||
|
return $(this).data('sid');
|
||||||
|
}).get();
|
||||||
|
updateAssetSystem(system_users)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,124 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,395 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a></li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-group-update' pk=asset_group.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 style="float: left"></span>{% trans 'Asset list of ' %} <b>{{ asset_group.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 table-hover " id="asset_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Hostname' %}</th>
|
||||||
|
<th>{% trans 'IP' %}</th>
|
||||||
|
<th>{% trans 'Port' %}</th>
|
||||||
|
<th>{% trans 'Type' %}</th>
|
||||||
|
<th>{% trans 'Alive' %}</th>
|
||||||
|
<th>{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{# {% for asset in assets %}#}
|
||||||
|
{# <tr id="bdg_asset" data-aid="{{ asset.id }}">#}
|
||||||
|
{# <td>{{ asset.hostname }}</td>#}
|
||||||
|
{# <td>{{ asset.ip }}</td>#}
|
||||||
|
{# <td>{{ asset.port }}</td>#}
|
||||||
|
{# {% if asset.is_active %}#}
|
||||||
|
{# <td><i class="fa fa-circle text-navy"></i></td>#}
|
||||||
|
{# {% else %}#}
|
||||||
|
{# <td><i class="fa fa-circle text-danger"></i></td>#}
|
||||||
|
{# {% endif %}#}
|
||||||
|
{# <td>#}
|
||||||
|
{# <a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-aid="{{ asset.id }}">{% trans "Delete" %}</a>#}
|
||||||
|
{# <a class="btn btn-xs btn-info m-l-xs btn_asset_update" data-aid="{{ asset.id }}" href="{% url 'assets:asset-update' pk=asset.id %}">{% trans "Update" %}</a>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# </tr>#}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Add asset to this group' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select assets' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset in assets_remain %}
|
||||||
|
<option value="{{ asset.id }}" id="opt_{{ asset.id }}">{{ asset.ip }}:{{ asset.port }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm btn-asset-add-groups">{% trans 'Add' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Associate system user' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table system-user-table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select system user' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for system_user in system_users_remain %}
|
||||||
|
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-info btn-sm btn-asset-add-groups-system-users">{% trans 'Associate' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
{% for system_user in system_users %}
|
||||||
|
<tr>
|
||||||
|
<td ><b class="bdg_system_user" data-sid={{ system_user.id }}>{{ system_user.name }}</b></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger btn-xs pull-right btn_leave_asset_group" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
jumpserver.system_users_selected = {};
|
||||||
|
function updateAssetsGroup(assets) {
|
||||||
|
var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
|
||||||
|
var body = {
|
||||||
|
assets: Object.assign([], assets)
|
||||||
|
};
|
||||||
|
var success = function(data) {
|
||||||
|
$('select2-selection__rendered').empty();
|
||||||
|
$('#groups_selected').val('');
|
||||||
|
$('#asset_list_table > tbody').empty();
|
||||||
|
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
||||||
|
var url = '{% url "api-assets:asset-detail" pk=99991937 %}'.replace(99991937, index);
|
||||||
|
asset = $.ajax({
|
||||||
|
url: url,
|
||||||
|
method: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data, textStatus) {
|
||||||
|
var add_tr = '<tr id="bdg_asset" data-aid="'+data.id+'">'+
|
||||||
|
'<td>'+data.hostname+'</td>'+
|
||||||
|
'<td>'+data.ip+'</td>'+
|
||||||
|
'<td>'+data.port+'</td>'+
|
||||||
|
'<td>status</td>'+
|
||||||
|
'<td>'+
|
||||||
|
'<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-aid="'+data.id+'">{% trans "Delete" %}</a>'+
|
||||||
|
'<a class="btn btn-xs btn-info m-l-xs btn_asset_update" data-aid="'+data.id+'" href="'+'{% url "assets:asset-update" pk=99991937 %}'.replace(99991937, data.id)+'">{% trans "Update" %}</a>'+
|
||||||
|
'</td>'+
|
||||||
|
'</tr>';
|
||||||
|
(data.is_active == true) ? tr = add_tr.replace('<td>status</td>', '<td><i class="fa fa-circle text-navy"></i></td>'): tr = add_tr.replace('<td>status</td>', '<td><i class="fa fa-circle text-danger"></i></td>');
|
||||||
|
$('#asset_list_table > tbody').append(tr);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectDelete(obj, name, url, data) {
|
||||||
|
function doDelete() {
|
||||||
|
var body = data;
|
||||||
|
var success = function() {
|
||||||
|
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
$(obj).parent().parent().remove();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure delete ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssetGroupSystemUsers(system_users) {
|
||||||
|
var the_url = "{% url 'api-assets:asset-groups-update-systemusers' pk=asset_group.id %}";
|
||||||
|
var body = {
|
||||||
|
system_users: Object.assign([], system_users)
|
||||||
|
};
|
||||||
|
var success = function(data) {
|
||||||
|
$('.select2-selection__rendered').empty();
|
||||||
|
$('#groups_selected').val('');
|
||||||
|
$.map(jumpserver.system_users_selected, function(system_user, index) {
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
$('.system-user-table tbody').append(
|
||||||
|
'<tr>' +
|
||||||
|
'<td><b class="bdg_group" data-sid="' + index + '">' + system_user + '</b></td>' +
|
||||||
|
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_group" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||||
|
'</tr>'
|
||||||
|
)
|
||||||
|
});
|
||||||
|
jumpserver.system_users_selected = {};
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.remove = function(val) {
|
||||||
|
var index = this.indexOf(val);
|
||||||
|
if (index > -1) {
|
||||||
|
this.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Array.prototype.unique = function(){
|
||||||
|
var res = [];
|
||||||
|
var json = {};
|
||||||
|
for(var i = 0; i < this.length; i++){
|
||||||
|
if(!json[this[i]]){
|
||||||
|
res.push(this[i]);
|
||||||
|
json[this[i]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2()
|
||||||
|
.on("select2:select", function (evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
jumpserver.assets_selected[data.id] = data.text;
|
||||||
|
jumpserver.system_users_selected[data.id] = data.text;
|
||||||
|
})
|
||||||
|
.on('select2:unselect', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
delete jumpserver.assets_selected[data.id];
|
||||||
|
delete jumpserver.system_users_selected[data.id]
|
||||||
|
});
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
ele: $('#asset_list_table'),
|
||||||
|
buttons: [],
|
||||||
|
order: [],
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 0, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}" data-aid="'+rowData.id+'">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 4, 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: 5, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:asset-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-aid="99991937">{% trans "Remove" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:asset-list" %}?asset_group_id={{ asset_group.id }}',
|
||||||
|
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||||
|
{data: "type" }, {data: "is_active" }, {data: "id"}],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-asset-add-groups', function () {
|
||||||
|
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jumpserver.system_users_selected = {};
|
||||||
|
var $data_table = $("#asset_list_table").DataTable();
|
||||||
|
var assets = [];
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:asset-list" %}?asset_group_id={{ asset_group.id }}',
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for(var i in result){
|
||||||
|
if (!isNaN(parseInt(result[i]['id']))) {
|
||||||
|
assets.push(parseInt(result[i]['id']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.map(jumpserver.assets_selected, function(value, index) {
|
||||||
|
assets.push(parseInt(index));
|
||||||
|
});
|
||||||
|
assets.unique();
|
||||||
|
var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
|
||||||
|
var body = {"assets": assets};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH'
|
||||||
|
});
|
||||||
|
{# TODO: reflash the table and reset the jumpserver variables #}
|
||||||
|
{# window.location.href='{% url "assets:asset-group-detail" pk=asset_group.id %}';#}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_asset_delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var the_url = "{% url 'api-assets:asset-groups-update' pk=asset_group.id %}";
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(1) > a").html();
|
||||||
|
var assets = [];
|
||||||
|
$('#asset_list_table > tbody > tr').map(function () {
|
||||||
|
assets.push(parseInt($(this).closest("tr").find(":nth-child(1) > a").attr("data-aid")))
|
||||||
|
});
|
||||||
|
var delete_asset_id = $(this).data('aid');
|
||||||
|
assets.remove(delete_asset_id);
|
||||||
|
var data = {"assets": assets};
|
||||||
|
objectDelete($this, name, the_url, data);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-asset-add-groups-system-users', function () {
|
||||||
|
if (Object.keys(jumpserver.system_users_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
var system_users = $('.bdg_system_user').map(function() {
|
||||||
|
return $(this).data('sid');
|
||||||
|
}).get();
|
||||||
|
$.map(jumpserver.system_users_selected, function(value, index) {
|
||||||
|
system_users.push(parseInt(index));
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
});
|
||||||
|
system_users.unique();
|
||||||
|
updateAssetGroupSystemUsers(system_users);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_leave_asset_group', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $tr = $this.closest('tr');
|
||||||
|
var $badge = $tr.find('.bdg_system_user');
|
||||||
|
var sid = $badge.data('sid');
|
||||||
|
var name = $badge.html() || $badge.text();
|
||||||
|
$('system-user-table').append(
|
||||||
|
'<option value="' + sid + '" id="opt_' + sid + '">' + name + '</option>'
|
||||||
|
);
|
||||||
|
$tr.remove();
|
||||||
|
var system_users = $('.bdg_system_user').map(function () {
|
||||||
|
return $(this).data('sid');
|
||||||
|
}).get();
|
||||||
|
updateAssetGroupSystemUsers(system_users)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,183 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
|
@ -0,0 +1,363 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load 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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.custom{
|
||||||
|
margin-right:5px;
|
||||||
|
}
|
||||||
|
#modal .modal-body { max-height: 200px; }
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content_left_head %}{% endblock %}
|
||||||
|
|
||||||
|
{% block table_search %}
|
||||||
|
<div class="html5buttons">
|
||||||
|
<div class="dt-buttons btn-group">
|
||||||
|
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0">
|
||||||
|
<span>{% trans "Import" %}</span>
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-default btn_export" tabindex="0">
|
||||||
|
<span>{% trans "Export" %}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block tags_list %}
|
||||||
|
<div class="ydxbd" id="ydxbd" style="display: none;">
|
||||||
|
<div class="tagBtnList">
|
||||||
|
{% for tag in tag_list %}
|
||||||
|
<a href="{% url 'assets:asset-tags' tag_id=tag.0 %}"
|
||||||
|
{% if tag.0 == tag_id %}
|
||||||
|
class="tagBtn2 label label-warning" name="tag_on">
|
||||||
|
{% else %}
|
||||||
|
class="tagBtn2 label label-default">
|
||||||
|
{% endif %}
|
||||||
|
{{ tag.1}}({{ tag.2 }})
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
<a href="{% url 'assets:asset-list' %}" class="tagBtn2 label label-default" >移除选择</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_container %}
|
||||||
|
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset" %} </a></div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="asset_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
|
||||||
|
<th class="text-center">{% trans 'Hostname' %}</th>
|
||||||
|
<th class="text-center">{% trans 'IP' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Port' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Type' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Env' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Hardware' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Valid' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Alive' %}</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>
|
||||||
|
<option value="deactive">{% trans 'Deactive selected' %}</option>
|
||||||
|
<option value="active">{% trans 'Active' %}</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_import_modal.html' %}
|
||||||
|
{% include 'assets/_asset_bulk_update_modal.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.onload = function (){
|
||||||
|
var tag_on = document.getElementsByName("tag_on");
|
||||||
|
var oDiv = document.getElementById("ydxbd");
|
||||||
|
if(tag_on.length > 0){
|
||||||
|
oDiv.style.display = "block";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function tagShow() {
|
||||||
|
var oDiv = document.getElementById("ydxbd");
|
||||||
|
if (oDiv.style.display == 'none'){
|
||||||
|
oDiv.style.display = "block";
|
||||||
|
}else{
|
||||||
|
oDiv.style.display = "none";
|
||||||
|
}
|
||||||
|
} //onload;
|
||||||
|
|
||||||
|
function objDelete(obj, name, url) {
|
||||||
|
function doDelete() {
|
||||||
|
var body = {};
|
||||||
|
var success = function() {
|
||||||
|
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
$(obj).parent().parent().remove();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'DELETE',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure delete ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
var options = {
|
||||||
|
ele: $('#asset_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 7, 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: 8, 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: 9, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:asset-update" 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_asset_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||||
|
columns: [{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||||
|
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware"},
|
||||||
|
{data: "is_active" }, {data: "is_active"}, {data: "id" }],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
var table = jumpserver.initDataTable(options);
|
||||||
|
$('.btn_export').click(function () {
|
||||||
|
var assets = [];
|
||||||
|
var rows = table.rows('.selected').data();
|
||||||
|
$.each(rows, function (index, obj) {
|
||||||
|
assets.push(obj.id)
|
||||||
|
});
|
||||||
|
console.log(assets);
|
||||||
|
$.ajax({
|
||||||
|
url: "{% url "assets:asset-export" %}",
|
||||||
|
method: 'POST',
|
||||||
|
data: JSON.stringify({assets_id: assets}),
|
||||||
|
dataType: "json",
|
||||||
|
success: function (data, textStatus) {
|
||||||
|
window.open(data.redirect)
|
||||||
|
},
|
||||||
|
error: function () {
|
||||||
|
toastr.error('Export failed');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
$('#btn_asset_import').click(function() {
|
||||||
|
var $form = $('#fm_asset_import');
|
||||||
|
$form.find('.help-block').remove();
|
||||||
|
function success (data) {
|
||||||
|
if (data.valid === false) {
|
||||||
|
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_assets'));
|
||||||
|
} else {
|
||||||
|
$('#id_created').html(data.created_info);
|
||||||
|
$('#id_created_detail').html(data.created.join(', '));
|
||||||
|
$('#id_updated').html(data.updated_info);
|
||||||
|
$('#id_updated_detail').html(data.updated.join(', '));
|
||||||
|
$('#id_failed').html(data.failed_info);
|
||||||
|
$('#id_failed_detail').html(data.failed.join(', '));
|
||||||
|
var $data_table = $('#asset_list_table').DataTable();
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$form.ajaxSubmit({success: success});
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_asset_delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $data_table = $("#asset_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-detail" pk=99991937 %}'.replace('99991937', uid);
|
||||||
|
console.log(the_url);
|
||||||
|
objDelete($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_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:asset-list' %}";
|
||||||
|
function doDeactive() {
|
||||||
|
var body = $.each(id_list, function(index, asset_object) {
|
||||||
|
asset_object['is_active'] = false;
|
||||||
|
});
|
||||||
|
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
jumpserver.checked = false;
|
||||||
|
}
|
||||||
|
function doActive() {
|
||||||
|
var body = $.each(id_list, function(index, asset_object) {
|
||||||
|
asset_object['is_active'] = true;
|
||||||
|
});
|
||||||
|
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
jumpserver.checked = false;
|
||||||
|
}
|
||||||
|
function doDelete() {
|
||||||
|
swal({
|
||||||
|
title: "{% trans 'Are you sure?' %}",
|
||||||
|
text: "{% trans 'This will delete the selected assets !!!' %}",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: "{% trans 'Confirm' %}",
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function() {
|
||||||
|
var success = function() {
|
||||||
|
var msg = "{% trans 'Asset Deleted.' %}";
|
||||||
|
swal("{% trans 'Asset Delete' %}", msg, "success");
|
||||||
|
$('#asset_list_table').DataTable().ajax.reload();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
var msg = "{% trans 'Asset Deleting failed.' %}";
|
||||||
|
swal("{% trans 'Asset 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_bulk_update_modal').modal('show');
|
||||||
|
}
|
||||||
|
switch(action) {
|
||||||
|
case 'deactive':
|
||||||
|
doDeactive();
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
doDelete();
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
doUpdate();
|
||||||
|
break;
|
||||||
|
case 'active':
|
||||||
|
doActive();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#btn_asset_bulk_update', function () {
|
||||||
|
var json_data = $("#fm_asset_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.groups != undefined) {
|
||||||
|
body.groups = json_data.groups;
|
||||||
|
}
|
||||||
|
if (typeof body.groups === 'string') {
|
||||||
|
body.groups = [parseInt(body.groups)]
|
||||||
|
} else if(typeof body.groups === 'array') {
|
||||||
|
var new_groups = body.groups.map(Number);
|
||||||
|
body.groups = new_groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_users = body.system_users.map(Number);
|
||||||
|
body.system_users = new_users;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_data.tags != undefined) {
|
||||||
|
body.tags = json_data.tags;
|
||||||
|
}
|
||||||
|
if (typeof body.tags == 'string') {
|
||||||
|
body.tags = [parseInt(body.tags)];
|
||||||
|
} else if (typeof body.tags === 'array') {
|
||||||
|
var new_tags = body.tags.map(Number);
|
||||||
|
body.tags = new_tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $data_table = $('#asset_list_table').DataTable();
|
||||||
|
var post_list = [];
|
||||||
|
$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-list' %}";
|
||||||
|
var success = function() {
|
||||||
|
var msg = "{% trans 'The selected assets has been updated successfully.' %}";
|
||||||
|
swal("{% trans 'Asset Updated' %}", msg, "success");
|
||||||
|
$('#asset_list_table').DataTable().ajax.reload();
|
||||||
|
jumpserver.checked = false;
|
||||||
|
};
|
||||||
|
console.log(JSON.stringify(post_list));
|
||||||
|
console.log(the_url);
|
||||||
|
{# APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});#}
|
||||||
|
$('#asset_bulk_update_modal').modal('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,133 @@
|
||||||
|
|
||||||
|
<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>
|
|
@ -0,0 +1,212 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,124 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,277 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </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>{{ asset_tag.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>{% trans 'Tag Name' %}:</td>
|
||||||
|
<td><b>{{ asset_tag.name }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ asset_tag.created_by }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date created' %}:</td>
|
||||||
|
<td><b>{{ asset_tag.created_time|date:"Y-m-d H:i:s" }}</b></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span style="float: left"></span>{% trans 'Asset list of ' %} <b>{{ asset_tag.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 table-striped table-bordered table-hover " id="tag_assets_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Hostname' %}</th>
|
||||||
|
<th>{% trans 'IP' %}</th>
|
||||||
|
<th>{% trans 'Port' %}</th>
|
||||||
|
<th>{% trans 'Type' %}</th>
|
||||||
|
<th>{% trans 'Valid' %}</th>
|
||||||
|
<th>{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Add asset to this tag' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset in assets_remain %}
|
||||||
|
<option value="{{ asset.id }}">{{ asset.ip }}:{{ asset.port }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-info btn-sm btn-tag-asset-add">{% trans 'Add' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
|
||||||
|
function updateTagAssets(assets) {
|
||||||
|
var the_url = "{% url 'api-assets:tag-update-assets' pk=tag.id %}";
|
||||||
|
var body = {
|
||||||
|
assets: Object.assign([], assets)
|
||||||
|
};
|
||||||
|
var $data_table = $("#tag_assets_table").DataTable();
|
||||||
|
var success = function(data) {
|
||||||
|
$('.select2-selection__rendered').empty();
|
||||||
|
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
});
|
||||||
|
jumpserver.groups_selected = {};
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PUT',
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function deleteTagAssets(obj, name, url, data) {
|
||||||
|
function doDelete() {
|
||||||
|
var body = data;
|
||||||
|
var success = function() {
|
||||||
|
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
$(obj).parent().parent().remove();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure delete ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Array.prototype.remove = function(val) {
|
||||||
|
var index = this.indexOf(val);
|
||||||
|
if (index > -1) {
|
||||||
|
this.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2()
|
||||||
|
.on("select2:select", function (evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
jumpserver.assets_selected[data.id] = data.text;
|
||||||
|
})
|
||||||
|
.on('select2:unselect', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
delete jumpserver.assets_selected[data.id];
|
||||||
|
});
|
||||||
|
var options = {
|
||||||
|
ele: $('#tag_assets_table'),
|
||||||
|
buttons: [],
|
||||||
|
order: [],
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 0, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}" data-aid="'+rowData.id+'">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 4, 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: 5, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:asset-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-aid="99991937">{% trans "Delete" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:asset-list" %}?tag_id={{ tag.id }}',
|
||||||
|
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||||
|
{data: "type" }, {data: "is_active" }, {data: "id"}],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-tag-asset-add', function () {
|
||||||
|
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var assets=[];
|
||||||
|
var $data_table = $("#tag_assets_table").DataTable();
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:asset-list" %}',
|
||||||
|
method: 'GET',
|
||||||
|
data: {"tag_id": {{ tag.id }}},
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for(var i in result){
|
||||||
|
if (!isNaN(parseInt(result[i]['id']))) {
|
||||||
|
assets.push(parseInt(result[i]['id']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.map(jumpserver.assets_selected, function(value, index) {
|
||||||
|
assets.push(parseInt(index));
|
||||||
|
});
|
||||||
|
updateTagAssets(assets);
|
||||||
|
$data_table.ajax().reload();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_asset_delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var the_url = "{% url 'api-assets:tag-update-assets' pk=tag.id %}";
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(1) > a").html();
|
||||||
|
var assets = [];
|
||||||
|
$('#tag_assets_table > tbody > tr').map(function () {
|
||||||
|
assets.push(parseInt($(this).closest("tr").find(":nth-child(1) > a").attr("data-aid")))
|
||||||
|
});
|
||||||
|
var delete_asset_id = $(this).data('aid');
|
||||||
|
assets.remove(delete_asset_id);
|
||||||
|
var data = {"assets": assets};
|
||||||
|
deleteTagAssets($this, name, the_url, data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,125 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,79 @@
|
||||||
|
{% extends '_base_create_update.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load bootstrap %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block custom_head_css_js_create %}
|
||||||
|
<link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet">
|
||||||
|
<script src="{% static "js/plugins/inputTags.jquery.min.js" %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% 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 'Hardware' %}</h3>
|
||||||
|
{{ form.sn|bootstrap_horizontal }}
|
||||||
|
{{ form.brand|bootstrap_horizontal }}
|
||||||
|
{{ form.cpu|bootstrap_horizontal }}
|
||||||
|
{{ form.memory|bootstrap_horizontal }}
|
||||||
|
{{ form.disk|bootstrap_horizontal }}
|
||||||
|
{{ form.mac_address|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<h3>{% trans 'Configuration' %}</h3>
|
||||||
|
{{ form.number|bootstrap_horizontal }}
|
||||||
|
{{ form.other_ip|bootstrap_horizontal }}
|
||||||
|
{{ form.remote_card_ip|bootstrap_horizontal }}
|
||||||
|
{{ form.os|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<h3>{% trans 'Location' %}</h3>
|
||||||
|
{{ form.cabinet_no|bootstrap_horizontal }}
|
||||||
|
{{ form.cabinet_pos|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<h3>{% trans 'Other' %}</h3>
|
||||||
|
{{ form.status|bootstrap_horizontal }}
|
||||||
|
{{ form.env|bootstrap_horizontal }}
|
||||||
|
{{ 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-white" 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();
|
||||||
|
$("#tags").select2({
|
||||||
|
tags: true,
|
||||||
|
maximumSelectionLength: 8 //最多能够选择的个数
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% 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>
|
|
@ -0,0 +1,366 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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 type="text/css">
|
||||||
|
|
||||||
|
</style>
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:idc-detail' pk=idc.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="active"><a href="{% url 'assets:idc-assets' pk=idc.id %}" class="text-center">
|
||||||
|
<i class="fa fa-bar-chart-o"></i> {% trans 'IDC assets' %}</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 style="float: left">{% trans 'IDC assets' %} <b>{{ idc.name }} </b><span class="badge"></span></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 table-striped table-bordered table-hover " id="idc_assets_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_all" class="ipt_check_all" >
|
||||||
|
</th>
|
||||||
|
<th>{% trans 'Hostname' %}</th>
|
||||||
|
<th>{% trans 'IP' %}</th>
|
||||||
|
<th>{% trans 'Port' %}</th>
|
||||||
|
<th>{% trans 'Type' %}</th>
|
||||||
|
<th>{% trans 'Valid' %}</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-warning">
|
||||||
|
{% trans 'Submit' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Attach assets to IDC ' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset in assets_remain %}
|
||||||
|
<option value="{{ asset.id }}" id="opt_{{ asset.id }}">{{ asset.ip}}:{{ asset.port }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm btn-asset-attach">{% trans 'Attach Assets' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include 'assets/_asset_bulk_update_modal.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
|
||||||
|
Array.prototype.remove = function(val) {
|
||||||
|
var index = this.indexOf(val);
|
||||||
|
if (index > -1) {
|
||||||
|
this.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function updateIDCAssets(assets) {
|
||||||
|
var the_url = "{% url 'api-assets:idc-update-assets' pk=idc.id %}";
|
||||||
|
var body = {
|
||||||
|
assets: Object.assign([], assets)
|
||||||
|
};
|
||||||
|
var $data_table = $("#idc_assets_table").DataTable();
|
||||||
|
var success = function(data) {
|
||||||
|
$('.select2-selection__rendered').empty();
|
||||||
|
$.map(jumpserver.assets_selected, function(asset_ip, index) {
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
});
|
||||||
|
jumpserver.groups_selected = {};
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PUT',
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteIDCAssets(assets) {
|
||||||
|
var the_url = "{% url 'api-assets:idc-update-assets' pk=idc.id %}";
|
||||||
|
var body = {
|
||||||
|
assets: Object.assign([], assets)
|
||||||
|
};
|
||||||
|
var $data_table = $("#idc_assets_table").DataTable();
|
||||||
|
var success = function(data) {
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PUT',
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2()
|
||||||
|
.on("select2:select", function (evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
jumpserver.assets_selected[data.id] = data.text;
|
||||||
|
})
|
||||||
|
.on('select2:unselect', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
delete jumpserver.assets_selected[data.id];
|
||||||
|
});
|
||||||
|
var options = {
|
||||||
|
ele: $('#idc_assets_table'),
|
||||||
|
buttons: [],
|
||||||
|
order: [],
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}" data-aid="'+rowData.id+'">' + 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>')
|
||||||
|
}
|
||||||
|
}}],
|
||||||
|
ajax_url: '{% url "api-assets:asset-list" %}?idc_id={{ idc.id }}',
|
||||||
|
columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||||
|
{data: "type" }, {data: "is_active" }],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-asset-attach', function () {
|
||||||
|
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var assets=[];
|
||||||
|
var $data_table = $("#idc_assets_table").DataTable();
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:asset-list" %}',
|
||||||
|
method: 'GET',
|
||||||
|
data: {"idc_id": {{ idc.id }}},
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for(var i in result){
|
||||||
|
if (!isNaN(parseInt(result[i]['id']))) {
|
||||||
|
assets.push(parseInt(result[i]['id']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.map(jumpserver.assets_selected, function(value, index) {
|
||||||
|
assets.push(parseInt(index));
|
||||||
|
});
|
||||||
|
updateIDCAssets(assets);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#btn_bulk_update', function () {
|
||||||
|
var action = $("#slct_bulk_update").val();
|
||||||
|
var $data_table = $("#idc_assets_table").DataTable();
|
||||||
|
var id_list = [];
|
||||||
|
var plain_id_list = [];
|
||||||
|
var assets = [];
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:asset-list" %}',
|
||||||
|
data: {"idc_id": {{ idc.id }}},
|
||||||
|
dataType: 'json',
|
||||||
|
method: 'GET',
|
||||||
|
success: function (result) {
|
||||||
|
for (var i in result) {
|
||||||
|
if (!isNaN(result[i]['id'])) {
|
||||||
|
assets.push(result[i]['id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var j in plain_id_list) {
|
||||||
|
assets.remove(plain_id_list[j])
|
||||||
|
}
|
||||||
|
function doDelete() {
|
||||||
|
swal({
|
||||||
|
title: "{% trans 'Are you sure?' %}",
|
||||||
|
text: "{% trans 'This will delete the selected assets !!!' %}",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: "{% trans 'Confirm' %}",
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function() {
|
||||||
|
var success = function() {
|
||||||
|
var msg = "{% trans 'Asset Deleted.' %}";
|
||||||
|
swal("{% trans 'Asset Delete' %}", msg, "success");
|
||||||
|
$('#idc_assets_table').DataTable().ajax.reload();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
var msg = "{% trans 'Asset Deleting failed.' %}";
|
||||||
|
swal("{% trans 'Asset Delete' %}", msg, "error");
|
||||||
|
};
|
||||||
|
var url_delete = "{% url 'api-assets:idc-update-assets' pk=idc.id %}";
|
||||||
|
var body = {
|
||||||
|
assets: Object.assign([], assets)
|
||||||
|
};
|
||||||
|
APIUpdateAttr({url: url_delete, body: JSON.stringify(body), method: 'PUT', success: success, error: fail});
|
||||||
|
jumpserver.checked = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function doUpdate() {
|
||||||
|
$('#asset_bulk_update_modal').modal('show');
|
||||||
|
}
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
doDelete();
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
doUpdate();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#btn_asset_bulk_update', function () {
|
||||||
|
var json_data = $("#fm_asset_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.groups != undefined) {
|
||||||
|
body.groups = json_data.groups;
|
||||||
|
}
|
||||||
|
if (typeof body.groups === 'string') {
|
||||||
|
body.groups = [parseInt(body.groups)]
|
||||||
|
} else if(typeof body.groups === 'array') {
|
||||||
|
var new_groups = body.groups.map(Number);
|
||||||
|
body.groups = new_groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_data.users != undefined) {
|
||||||
|
body.users = json_data.users;
|
||||||
|
}
|
||||||
|
if (typeof body.users === 'string') {
|
||||||
|
body.users = [parseInt(body.users)]
|
||||||
|
} else if(typeof body.users === 'array') {
|
||||||
|
var new_users = body.users.map(Number);
|
||||||
|
body.users = new_users;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_data.tags != undefined) {
|
||||||
|
body.tags = json_data.tags;
|
||||||
|
}
|
||||||
|
if (typeof body.tags == 'string') {
|
||||||
|
body.tags = [parseInt(body.tags)];
|
||||||
|
} else if (typeof body.tags === 'array') {
|
||||||
|
var new_tags = body.tags.map(Number);
|
||||||
|
body.tags = new_tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $data_table = $('#asset_list_table').DataTable();
|
||||||
|
var post_list = [];
|
||||||
|
$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-list' %}";
|
||||||
|
var success = function() {
|
||||||
|
var msg = "{% trans 'The selected assets has been updated successfully.' %}";
|
||||||
|
swal("{% trans 'Asset Updated' %}", msg, "success");
|
||||||
|
$('#asset_list_table').DataTable().ajax.reload();
|
||||||
|
jumpserver.checked = false;
|
||||||
|
};
|
||||||
|
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
|
||||||
|
$('#asset_bulk_update_modal').modal('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,73 @@
|
||||||
|
{% 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="IDCForm" method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
<h3 class="widget-head-color-box">基本信息</h3>
|
||||||
|
{{ form.name|bootstrap_horizontal }}
|
||||||
|
{{ form.address|bootstrap_horizontal }}
|
||||||
|
{{ form.contact|bootstrap_horizontal }}
|
||||||
|
{{ form.phone|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></div>
|
||||||
|
<h3 class="widget-head-color-box">IP段</h3>
|
||||||
|
{{ form.operator|bootstrap_horizontal }}
|
||||||
|
{{ form.intranet|bootstrap_horizontal }}
|
||||||
|
{{ form.extranet|bootstrap_horizontal }}
|
||||||
|
|
||||||
|
<div class="hr-line-dashed"></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>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,156 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'assets:idc-detail' pk=idc.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:idc-assets' pk=idc.id %}" class="text-center">
|
||||||
|
<i class="fa fa-bar-chart-o"></i> {% trans 'IDC assets' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:idc-update' pk=idc.id %}"><i class="fa fa-edit"></i>Update</a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-danger btn-delete-idc">
|
||||||
|
<i class="fa fa-edit"></i>Delete
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="col-sm-9" style="padding-left: 0;">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span class="label"><b>{{ idc.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 idc-details">
|
||||||
|
<tbody>
|
||||||
|
<tr class="no-borders-tr" data-name="{{ idc.name }}">
|
||||||
|
<td>{% trans 'Name' %}:</td>
|
||||||
|
<td><b>{{ idc.name }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Bandwidth' %}:</td>
|
||||||
|
<td><b>{{ idc.bandwidth }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Contact' %}:</td>
|
||||||
|
<td><b>{{ idc.contact }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Phone' %}:</td>
|
||||||
|
<td><b>{{ idc.phone }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Address' %}:</td>
|
||||||
|
<td><b>{{ idc.address }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Intranet' %}:</td>
|
||||||
|
<td><b>{{ idc.Intranet }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Extranet' %}:</td>
|
||||||
|
<td><b>{{ idc.extranet }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Operator' %}:</td>
|
||||||
|
<td><b>{{ idc.operator }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date created' %}:</td>
|
||||||
|
<td><b>{{ system_user.date_created }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ asset_group.created_by }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Comment' %}:</td>
|
||||||
|
<td><b>{{ system_user.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
function idcDelete(name, url) {
|
||||||
|
function doDelete() {
|
||||||
|
var body = {};
|
||||||
|
var success = function() {
|
||||||
|
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
window.location.href="{% url 'assets:idc-list' %}";
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'DELETE',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure delete ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2();
|
||||||
|
})
|
||||||
|
.on('click', '.btn-delete-idc', function () {
|
||||||
|
var name = $('.idc-details > tbody > tr').attr("data-name");
|
||||||
|
var id = {{ idc.id }};
|
||||||
|
var the_url = '{% url "api-assets:idc-detail" pk=99991937 %}'.replace(99991937, id);
|
||||||
|
idcDelete(name, the_url);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,128 @@
|
||||||
|
{% 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 table_search %}{% endblock %}
|
||||||
|
{% block table_container %}
|
||||||
|
<div class="uc pull-left m-l-5 m-r-5">
|
||||||
|
<a href="{% url "assets:idc-create" %}" class="btn btn-sm btn-primary"> {% trans "Create IDC" %} </a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="idc_list_table" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-center">
|
||||||
|
<input type="checkbox" id="check_all" class="ipt_check_all" >
|
||||||
|
</th>
|
||||||
|
<th class="text-center"><a href="{% url 'assets:idc-list' %}?sort=name">{% trans 'Name' %}</a></th>
|
||||||
|
<th class="text-center">{% trans 'Asset num' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Contact' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Phone' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Operator' %}</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-warning">
|
||||||
|
{% trans 'Submit' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block content_bottom_left %}{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
var options = {
|
||||||
|
ele: $('#idc_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:idc-detail" pk=99991937 %}">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
|
||||||
|
{targets: 6, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:idc-update" 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_idc_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}],
|
||||||
|
ajax_url: '{% url "api-assets:idc-list" %}',
|
||||||
|
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }, {data: "contact" }, {data: "phone" },
|
||||||
|
{data: "operator" }, {data: "id" }],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_idc_delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $data_table = $('#idc_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:idc-detail" pk=99991937 %}'.replace('99991937', uid);
|
||||||
|
objectDelete($this, name, the_url);
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
{# TODO: reload the tale #}
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#btn_bulk_update', function () {
|
||||||
|
var action = $('#slct_bulk_update').val();
|
||||||
|
var $data_table = $('#idc_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:idc-list' %}";
|
||||||
|
function doDelete() {
|
||||||
|
swal({
|
||||||
|
title: "{% trans 'Are you sure?' %}",
|
||||||
|
text: "{% trans 'This will delete the selected idc !!!' %}",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: "{% trans 'Confirm' %}",
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function() {
|
||||||
|
var success = function() {
|
||||||
|
var msg = "{% trans 'IDC Deleted.' %}";
|
||||||
|
swal("{% trans 'IDC Delete' %}", msg, "success");
|
||||||
|
$('#idc_list_table').DataTable().ajax.reload();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
var msg = "{% trans 'IDC Deleting failed.' %}";
|
||||||
|
swal("{% trans 'IDC 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
doDelete();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,371 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li class="active"><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||||
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets and asset groups' %}</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 style="float: left">{% trans 'Assets attached of ' %} <b>{{ system_user.name }} </b><span class="badge">{{ paginator.count }}</span></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 table-hover" id="system_user_list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Hostname' %}</th>
|
||||||
|
<th>{% trans 'IP' %}</th>
|
||||||
|
<th>{% trans 'Port' %}</th>
|
||||||
|
<th>{% trans 'Reachable' %}</th>
|
||||||
|
<th>{% trans 'Action' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{# {% for asset in page_obj %}#}
|
||||||
|
{# <tr>#}
|
||||||
|
{# <td>{{ asset.hostname }}</td>#}
|
||||||
|
{# <td>{{ asset.ip }}</td>#}
|
||||||
|
{# <td>{{ asset.port }}</td>#}
|
||||||
|
{# <td>#}
|
||||||
|
{# <i class="fa fa-check text-navy"></i>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# <td>#}
|
||||||
|
{# <button class="btn btn-danger pull-right btn-xs {% if asset.is_inherit_from_asset_groups %} disabled {% endif %}" type="button"><i class="fa fa-minus"></i></button>#}
|
||||||
|
{# </td>#}
|
||||||
|
{# </tr>#}
|
||||||
|
{# {% endfor %}#}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{# <div class="row">#}
|
||||||
|
{# {% include '_pagination.html' %}#}
|
||||||
|
{# </div>#}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Attach to assets ' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset in assets_remain %}
|
||||||
|
<option value="{{ asset.id }}">{{ asset.ip}}:{{ asset.port }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm btn-add-asset2system-user">{% trans 'Attach Asset' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-info">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Attach to asset groups' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table group_edit">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<select data-placeholder="{% trans 'Add asset group' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset_group in asset_groups_remain %}
|
||||||
|
<option value="{{ asset_group.id }}">{{ asset_group.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="no-borders">
|
||||||
|
<button type="button" class="btn btn-info btn-sm" id="btn_add_user_group">{% trans 'Attach AssetGroup' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<tr>
|
||||||
|
<td ><b class="bdg_asset_groups" data-gid={{ asset_group.id }}>{{ asset_group.name }}</b></td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger pull-right btn-xs btn-leave-system_user" type="button"><i class="fa fa-minus"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
jumpserver.asset_groups_selected = {};
|
||||||
|
Array.prototype.remove = function(val) {
|
||||||
|
var index = this.indexOf(val);
|
||||||
|
if (index > -1) {
|
||||||
|
this.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Array.prototype.unique = function(){
|
||||||
|
var res = [];
|
||||||
|
var json = {};
|
||||||
|
for(var i = 0; i < this.length; i++){
|
||||||
|
if(!json[this[i]]){
|
||||||
|
res.push(this[i]);
|
||||||
|
json[this[i]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
function objectDelete(obj, name, url, data) {
|
||||||
|
function doDelete() {
|
||||||
|
var body = data;
|
||||||
|
var success = function() {
|
||||||
|
swal('Deleted!', "[ "+name+"]"+" has been deleted ", "success");
|
||||||
|
$(obj).parent().parent().remove();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
swal("Failed", "Delete"+"[ "+name+" ]"+"failed", "error");
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH',
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: 'Are you sure delete ?',
|
||||||
|
text: " [" + name + "] ",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: 'Cancel',
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: 'Confirm',
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function () {
|
||||||
|
doDelete()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function updateSystemUserAssetGroup(asset_groups) {
|
||||||
|
var the_url = "{% url 'api-assets:systemuser-update-assetgroups' pk=system_user.id %}";
|
||||||
|
var body = {
|
||||||
|
asset_groups: Object.assign([], asset_groups)
|
||||||
|
};
|
||||||
|
var success = function(data) {
|
||||||
|
$('.select2-selection__rendered').empty();
|
||||||
|
$('#groups_selected').val('');
|
||||||
|
$.map(jumpserver.asset_groups_selected, function(asset_groups, index) {
|
||||||
|
$('#opt_' + index).remove();
|
||||||
|
$('.system-user-table tbody').append(
|
||||||
|
'<tr>' +
|
||||||
|
'<td><b class="bdg_asset_groups" data-sid="' + index + '">' + asset_groups + '</b></td>' +
|
||||||
|
'<td><button class="btn btn-danger btn-xs pull-right btn-leave-system_user" type="button"><i class="fa fa-minus"></i></button></td>' +
|
||||||
|
'</tr>'
|
||||||
|
)
|
||||||
|
});
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
success: success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2()
|
||||||
|
.on("select2:select", function (evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
jumpserver.assets_selected[data.id] = data.text;
|
||||||
|
jumpserver.asset_groups_selected[data.id] = data.text;
|
||||||
|
})
|
||||||
|
.on('select2:unselect', function(evt) {
|
||||||
|
var data = evt.params.data;
|
||||||
|
delete jumpserver.assets_selected[data.id];
|
||||||
|
delete jumpserver.asset_groups_selected[data.id];
|
||||||
|
});
|
||||||
|
var options = {
|
||||||
|
ele: $('#system_user_list'),
|
||||||
|
buttons: [],
|
||||||
|
order: [],
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 0, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:asset-detail" pk=99991937 %}" data-aid="'+rowData.id+'">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 3, 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: 4, createdCell: function (td, cellData, rowData) {
|
||||||
|
var update_btn = '<a href="{% url "assets:asset-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-aid="99991937">{% trans "Delete" %}</a>'.replace('99991937', rowData.id);
|
||||||
|
$(td).html(update_btn + del_btn)
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
ajax_url: '{% url "api-assets:asset-list" %}?system_user_id={{ system_user.id }}',
|
||||||
|
columns: [{data: "hostname" }, {data: "ip" }, {data: "port" }, {data: function () { return ""; } }, {data: "id"}],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-add-asset2system-user', function () {
|
||||||
|
if (Object.keys(jumpserver.assets_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var $data_table = $("#system_user_list").DataTable();
|
||||||
|
var assets = [];
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:asset-list" %}?system_user_id={{ system_user.id }}',
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for(var i in result){
|
||||||
|
if (!isNaN(parseInt(result[i]['id']))) {
|
||||||
|
assets.push(parseInt(result[i]['id']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.map(jumpserver.assets_selected, function(value, index) {
|
||||||
|
assets.push(parseInt(index));
|
||||||
|
});
|
||||||
|
assets.unique();
|
||||||
|
var the_url = "{% url 'api-assets:systemuser-update-assets' pk=system_user.id %}";
|
||||||
|
var body = {"assets": assets};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH'
|
||||||
|
});
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_asset_delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var the_url = "{% url 'api-assets:systemuser-update-assets' pk=system_user.id %}";
|
||||||
|
var name = $(this).closest("tr").find(":nth-child(1) > a").html();
|
||||||
|
var $data_table = $("#system_user_list").DataTable();
|
||||||
|
var assets = [];
|
||||||
|
$('#system_user_list > tbody > tr').map(function () {
|
||||||
|
assets.push(parseInt($(this).closest("tr").find(":nth-child(1) > a").attr("data-aid")))
|
||||||
|
});
|
||||||
|
var delete_asset_id = $(this).data('aid');
|
||||||
|
assets.remove(delete_asset_id);
|
||||||
|
assets.unique();
|
||||||
|
var data = {"assets": assets};
|
||||||
|
objectDelete($this, name, the_url, data);
|
||||||
|
$data_table.ajax.reload();
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '#btn_add_user_group', function () {
|
||||||
|
jumpserver.assets_selected = {};
|
||||||
|
if (Object.keys(jumpserver.asset_groups_selected).length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
asset_groups = [];
|
||||||
|
$.ajax({
|
||||||
|
url: '{% url "api-assets:systemuser-update-assetgroups" pk=system_user.id %}',
|
||||||
|
method: 'GET',
|
||||||
|
dataType: 'json',
|
||||||
|
success: function (result) {
|
||||||
|
for (var i in result['asset_groups']) {
|
||||||
|
if (!isNaN(result['asset_groups'][i])) {
|
||||||
|
asset_groups.push(parseInt(result['asset_groups'][i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$.map(jumpserver.asset_groups_selected, function(value, index) {
|
||||||
|
asset_groups.push(parseInt(index));
|
||||||
|
});
|
||||||
|
asset_groups.unique();
|
||||||
|
console.log(asset_groups);
|
||||||
|
var the_url = '{% url "api-assets:systemuser-update-assetgroups" pk=system_user.id %}';
|
||||||
|
var body = {"asset_groups": asset_groups};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: the_url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'PATCH'
|
||||||
|
});
|
||||||
|
{# TODO: reload the table #}
|
||||||
|
{# window.location.href="{% url 'assets:system-user-asset' pk=system_user.id %}"#}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn-leave-system_user', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $tr = $this.closest('tr');
|
||||||
|
var $badge = $tr.find('.bdg_asset_groups');
|
||||||
|
var sid = $badge.data('gid');
|
||||||
|
var name = $badge.html() || $badge.text();
|
||||||
|
$('system-user-table').append(
|
||||||
|
'<option value="' + sid + '" id="opt_' + sid + '">' + name + '</option>'
|
||||||
|
);
|
||||||
|
$tr.remove();
|
||||||
|
var asset_groups = $('.bdg_asset_groups').map(function () {
|
||||||
|
return $(this).data('gid');
|
||||||
|
}).get();
|
||||||
|
updateSystemUserAssetGroup(asset_groups);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,134 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li><a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||||
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets' %}</a>
|
||||||
|
</li>
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'assets:system-user-asset-group' pk=system_user.id %}" class="text-center">
|
||||||
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate asset groups' %}</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 style="float: left">{% trans 'Asset list of ' %} <b>{{ admin_user.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 table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Name' %}</th>
|
||||||
|
<th>{% trans 'Asset num' %}</th>
|
||||||
|
<th>{% trans 'Unavailable num' %}</th>
|
||||||
|
<th>{% trans 'Comment' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for asset_group in page_obj %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ asset_group.name }}</td>
|
||||||
|
<td>{{ asset_group_group.assets.count }}</td>
|
||||||
|
<td>{{ asset_group_group.assets.count }}</td>
|
||||||
|
<td>{{ asset_group.comment|truncatewords:4 }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<div class="row">
|
||||||
|
{% include '_pagination.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Add asset group to this system user' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<form>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<select data-placeholder="{% trans 'Select asset group' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
|
||||||
|
{% for asset_group in asset_groups %}
|
||||||
|
<option value="{{ asset_group.id }}">{{ asset_group.name}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td colspan="2">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm">{% trans 'Add' %}</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</form>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
{# function switch_user_status(obj) {#}
|
||||||
|
{# var status = $(obj).prop('checked');#}
|
||||||
|
{##}
|
||||||
|
{# $.ajax({#}
|
||||||
|
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||||
|
{# type: "PUT",#}
|
||||||
|
{# data: {#}
|
||||||
|
{# 'is_active': status#}
|
||||||
|
{# },#}
|
||||||
|
{# success: function (data, status) {#}
|
||||||
|
{# console.log(data)#}
|
||||||
|
{# },#}
|
||||||
|
{# error: function () {#}
|
||||||
|
{# console.log('error')#}
|
||||||
|
{# }#}
|
||||||
|
{# })#}
|
||||||
|
{# }#}
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,123 @@
|
||||||
|
{% 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 system 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.no_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 %}
|
||||||
|
<h3>{% trans 'Basic' %}</h3>
|
||||||
|
{{ form.name|bootstrap_horizontal }}
|
||||||
|
{{ form.username|bootstrap_horizontal }}
|
||||||
|
{{ form.protocol|bootstrap_horizontal }}
|
||||||
|
<h3>{% trans 'Auth' %}</h3>
|
||||||
|
{{ form.auth_method|bootstrap_horizontal }}
|
||||||
|
<div class="password-auth hidden">
|
||||||
|
{{ form.password|bootstrap_horizontal }}
|
||||||
|
</div>
|
||||||
|
<div class="public-key-auth">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
{{ form.auto_generate_key}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ form.private_key_file|bootstrap_horizontal }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.as_push.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto push' %}</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
{{ form.auto_push}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3>{% trans 'Other' %}</h3>
|
||||||
|
{{ form.sudo|bootstrap_horizontal }}
|
||||||
|
{{ form.home|bootstrap_horizontal }}
|
||||||
|
{{ form.shell|bootstrap_horizontal }}
|
||||||
|
{{ form.uid|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>
|
||||||
|
var auth_method = '#'+'{{ form.auth_method.id_for_label }}';
|
||||||
|
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
|
||||||
|
function authMethodDisplay() {
|
||||||
|
if ($(auth_method).val() == 'P') {
|
||||||
|
$('.password-auth').removeClass('hidden');
|
||||||
|
$('.public-key-auth').addClass('hidden');
|
||||||
|
$('#'+'{{ form.password.id_for_label }}').attr('required', 'required');
|
||||||
|
|
||||||
|
} else if ($(auth_method).val() == 'K') {
|
||||||
|
$('.password-auth').addClass('hidden');
|
||||||
|
$('.public-key-auth').removeClass('hidden');
|
||||||
|
|
||||||
|
if ($(auto_generate_key).prop('checked')){
|
||||||
|
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').addClass('hidden');
|
||||||
|
} else {
|
||||||
|
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').removeClass('hidden');
|
||||||
|
{# $('#'+'{{ form.private_key_file.id_for_label }}').attr('required', 'required');#}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2();
|
||||||
|
authMethodDisplay();
|
||||||
|
$(auth_method).change(function () {
|
||||||
|
authMethodDisplay();
|
||||||
|
});
|
||||||
|
$(auto_generate_key).change(function () {
|
||||||
|
authMethodDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
if ($('#'+'{{ form.protocol.id_for_label }}').val() == 'telnet') {
|
||||||
|
$('#'+'{{ form.auto_generate_key.id_for_label }}').closest('.form-group').remove();
|
||||||
|
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').remove();
|
||||||
|
$('#'+'{{ form.auto_push.id_for_label }}').closest('.form-group').remove();
|
||||||
|
$('#'+'{{ form.auto_update.id_for_label }}').closest('.form-group').remove();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,185 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% 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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="{% url 'assets:system-user-detail' pk=system_user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Detail' %} </a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'assets:system-user-asset' pk=system_user.id %}" class="text-center">
|
||||||
|
<i class="fa fa-bar-chart-o"></i> {% trans 'Associate assets and asset groups' %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="pull-right">
|
||||||
|
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.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>{{ system_user.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>{% trans 'Name' %}:</td>
|
||||||
|
<td><b>{{ system_user.name }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Username' %}:</td>
|
||||||
|
<td><b>{{ system_user.username }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Protocol' %}:</td>
|
||||||
|
<td><b>{{ system_user.protocol }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Auto push' %}:</td>
|
||||||
|
<td><b>{{ system_user.protocol }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Auto update' %}:</td>
|
||||||
|
<td><b>{{ system_user.auto_update }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'As default' %}:</td>
|
||||||
|
<td><b>{{ system_user.protocol }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Sudo' %}:</td>
|
||||||
|
<td><b>{{ system_user.sudo }}</b></td>
|
||||||
|
</tr>
|
||||||
|
{% if system_user.shell %}
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Shell' %}:</td>
|
||||||
|
<td><b>{{ system_user.shell }}</b></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if system_user.home %}
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Home' %}:</td>
|
||||||
|
<td><b>{{ system_user.home }}</b></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if system_user.uid %}
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Uid' %}:</td>
|
||||||
|
<td><b>{{ system_user.uid }}</b></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Date created' %}:</td>
|
||||||
|
<td><b>{{ system_user.date_created }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Created by' %}:</td>
|
||||||
|
<td><b>{{ asset_group.created_by }}</b></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{% trans 'Comment' %}:</td>
|
||||||
|
<td><b>{{ system_user.comment }}</b></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
|
<div class="panel panel-primary">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<i class="fa fa-info-circle"></i> {% trans 'Quick update' %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr class="no-borders-tr">
|
||||||
|
<td width="50%">{% trans 'Get mannual install script' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span style="float: right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Get' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="50%">{% trans 'Retest asset connectivity' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span style="float: right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Start' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td width="50%">{% trans 'Reset private key' %}:</td>
|
||||||
|
<td>
|
||||||
|
<span style="float: right">
|
||||||
|
<button type="button" class="btn btn-primary btn-xs" style="width: 54px">{% trans 'Reset' %}</button>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
{# function switch_user_status(obj) {#}
|
||||||
|
{# var status = $(obj).prop('checked');#}
|
||||||
|
{##}
|
||||||
|
{# $.ajax({#}
|
||||||
|
{# url: "{% url 'users:user-active-api' pk=user.id %}",#}
|
||||||
|
{# type: "PUT",#}
|
||||||
|
{# data: {#}
|
||||||
|
{# 'is_active': status#}
|
||||||
|
{# },#}
|
||||||
|
{# success: function (data, status) {#}
|
||||||
|
{# console.log(data)#}
|
||||||
|
{# },#}
|
||||||
|
{# error: function () {#}
|
||||||
|
{# console.log('error')#}
|
||||||
|
{# }#}
|
||||||
|
{# })#}
|
||||||
|
{# }#}
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.select2').select2();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,138 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block table_search %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_container %}
|
||||||
|
<div class="uc pull-left m-l-5 m-r-5">
|
||||||
|
<a href="{% url 'assets:system-user-create' %}" class="btn btn-sm btn-primary "> {% trans "Create system user" %} </a>
|
||||||
|
</div>
|
||||||
|
<table class="table table-striped table-bordered table-hover " id="system_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' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Unreachable' %}</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-warning">
|
||||||
|
{% trans 'Submit' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
var options = {
|
||||||
|
ele: $('#system_user_list_table'),
|
||||||
|
columnDefs: [
|
||||||
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
|
var detail_btn = '<a href="{% url "assets:system-user-detail" pk=99991937 %}">' + cellData + '</a>';
|
||||||
|
$(td).html(detail_btn.replace('99991937', rowData.id));
|
||||||
|
}},
|
||||||
|
{targets: 5, createdCell: function (td, cellData) {
|
||||||
|
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
|
||||||
|
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
|
||||||
|
}},
|
||||||
|
{targets: 6, createdCell: function (td, cellData, rowData) {
|
||||||
|
var script_btn = '<a href="{% url "assets:system-user-update" pk=99991937 %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'.replace('99991937', cellData);
|
||||||
|
var update_btn = '<a href="{% url "assets:system-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)
|
||||||
|
}}],
|
||||||
|
ajax_url: '{% url "api-assets:system-user-list" %}',
|
||||||
|
columns: [{data: "id" }, {data: "name" }, {data: "username" }, {data: "assets_amount" }, {data: function () { return "3"}},
|
||||||
|
{data: "comment" }, {data: "id" }],
|
||||||
|
op_html: $('#actions').html()
|
||||||
|
};
|
||||||
|
jumpserver.initDataTable(options);
|
||||||
|
})
|
||||||
|
|
||||||
|
.on('click', '.btn_admin_user_delete', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var $data_table = $('#idc_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:system-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 = $('#system_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 (id_list === []) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var the_url = "{% url 'api-assets:system-user-list' %}";
|
||||||
|
function doDelete() {
|
||||||
|
swal({
|
||||||
|
title: "{% trans 'Are you sure?' %}",
|
||||||
|
text: "{% trans 'This will delete the selected System Users !!!' %}",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonColor: "#DD6B55",
|
||||||
|
confirmButtonText: "{% trans 'Confirm' %}",
|
||||||
|
closeOnConfirm: false
|
||||||
|
}, function() {
|
||||||
|
var success = function() {
|
||||||
|
var msg = "{% trans 'System Users Deleted.' %}";
|
||||||
|
swal("{% trans 'System Users Delete' %}", msg, "success");
|
||||||
|
$('#system_user_list_table').DataTable().ajax.reload();
|
||||||
|
};
|
||||||
|
var fail = function() {
|
||||||
|
var msg = "{% trans 'System Users Deleting failed.' %}";
|
||||||
|
swal("{% trans 'System 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() {
|
||||||
|
{# TODO: bulk update the System Users #}
|
||||||
|
}
|
||||||
|
switch (action) {
|
||||||
|
case 'delete':
|
||||||
|
doDelete();
|
||||||
|
break;
|
||||||
|
case 'update':
|
||||||
|
doUpdate();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.conf import settings
|
||||||
|
register = template.Library()
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# coding:utf-8
|
||||||
|
from django.conf.urls import url
|
||||||
|
from .. import api
|
||||||
|
from rest_framework import routers
|
||||||
|
from rest_framework_bulk.routes import BulkRouter
|
||||||
|
|
||||||
|
app_name = 'assets'
|
||||||
|
|
||||||
|
|
||||||
|
router = BulkRouter()
|
||||||
|
router.register(r'v1/asset-groups', api.AssetGroupViewSet, 'asset-group')
|
||||||
|
router.register(r'v1/assets', api.AssetViewSet, 'asset')
|
||||||
|
router.register(r'v1/idc', api.IDCViewSet, 'idc')
|
||||||
|
router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user')
|
||||||
|
router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
|
||||||
|
router.register(r'v1/tags', api.TagViewSet, 'asset-tag')
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
||||||
|
# url(r'^v1/idc/(?P<pk>[0-9]+)/assets/$', api.IDCAssetsApi.as_view(), name='api-idc-assets'),
|
||||||
|
url(r'^v1/system-user/(?P<pk>[0-9]+)/auth-info/', api.SystemUserAuthInfoApi.as_view(),
|
||||||
|
name='system-user-auth-info'),
|
||||||
|
url(r'^v1/assets/(?P<pk>\d+)/groups/$',
|
||||||
|
api.AssetUpdateGroupApi.as_view(), name='asset-update-group'),
|
||||||
|
|
||||||
|
url(r'^v1/assets/(?P<pk>\d+)/system-users/$',
|
||||||
|
api.SystemUserUpdateApi.as_view(), name='asset-update-system-users'),
|
||||||
|
|
||||||
|
# update the system users, which add and delete the asset to the system user
|
||||||
|
url(r'^v1/system_user/(?P<pk>\d+)/assets/$',
|
||||||
|
api.SystemUserUpdateAssetsApi.as_view(), name='systemuser-update-assets'),
|
||||||
|
|
||||||
|
url(r'^v1/system_user/(?P<pk>\d+)/groups/$',
|
||||||
|
api.SystemUserUpdateAssetGroupApi.as_view(), name='systemuser-update-assetgroups'),
|
||||||
|
|
||||||
|
# update the asset group, which add or delete the asset to the group
|
||||||
|
url(r'^v1/groups/(?P<pk>\d+)/assets/$',
|
||||||
|
api.AssetGroupUpdateApi.as_view(), name='asset-groups-update'),
|
||||||
|
|
||||||
|
# update the asset group, and add or delete the system_user to the group
|
||||||
|
url(r'^v1/groups/(?P<pk>\d+)/system-users/$',
|
||||||
|
api.AssetGroupUpdateSystemUserApi.as_view(), name='asset-groups-update-systemusers'),
|
||||||
|
|
||||||
|
# update the IDC, and add or delete the assets to the IDC
|
||||||
|
url(r'^v1/idc/(?P<pk>\d+)/assets/$',
|
||||||
|
api.IDCupdateAssetsApi.as_view(), name='idc-update-assets'),
|
||||||
|
|
||||||
|
url(r'v1/tag/(?P<pk>\d+)/assets/$',
|
||||||
|
api.TagUpdateAssetsApi.as_view(), name='tag-update-assets'),
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# coding:utf-8
|
||||||
|
from django.conf.urls import url
|
||||||
|
from .. import views
|
||||||
|
|
||||||
|
app_name = 'assets'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Resource asset url
|
||||||
|
url(r'^$', views.AssetListView.as_view(), name='asset-index'),
|
||||||
|
url(r'^asset/$', views.AssetListView.as_view(), name='asset-list'),
|
||||||
|
url(r'^asset/create/$', views.AssetCreateView.as_view(), name='asset-create'),
|
||||||
|
url(r'^asset/export/$', views.AssetExportView.as_view(), name='asset-export'),
|
||||||
|
url(r'^asset/import/$', views.BulkImportAssetView.as_view(), name='asset-import'),
|
||||||
|
url(r'^asset/(?P<pk>[0-9]+)/$', views.AssetDetailView.as_view(), name='asset-detail'),
|
||||||
|
url(r'^asset/(?P<pk>[0-9]+)/update/$', views.AssetUpdateView.as_view(), name='asset-update'),
|
||||||
|
url(r'^asset/(?P<pk>[0-9]+)/delete/$', views.AssetDeleteView.as_view(), name='asset-delete'),
|
||||||
|
url(r'^asset-modal$', views.AssetModalListView.as_view(), name='asset-modal-list'),
|
||||||
|
url(r'^asset-modal-update$', views.AssetModalCreateView.as_view(), name='asset-modal-update'),
|
||||||
|
|
||||||
|
# Resource asset group url
|
||||||
|
url(r'^asset-group/$', views.AssetGroupListView.as_view(), name='asset-group-list'),
|
||||||
|
url(r'^asset-group/create/$', views.AssetGroupCreateView.as_view(), name='asset-group-create'),
|
||||||
|
url(r'^asset-group/(?P<pk>[0-9]+)/$', views.AssetGroupDetailView.as_view(), name='asset-group-detail'),
|
||||||
|
url(r'^asset-group/(?P<pk>[0-9]+)/update/$', views.AssetGroupUpdateView.as_view(), name='asset-group-update'),
|
||||||
|
url(r'^asset-group/(?P<pk>[0-9]+)/delete/$', views.AssetGroupDeleteView.as_view(), name='asset-group-delete'),
|
||||||
|
|
||||||
|
url(r'^tags/$', views.TagsListView.as_view(), name='asset-tag-list'),
|
||||||
|
url(r'^asset-by-tag/(?P<tag_id>[0-9]+)/$', views.TagView.as_view(), name='asset-tags'),
|
||||||
|
url(r'^tags/create/$', views.AssetTagCreateView.as_view(), name='asset-tag-create'),
|
||||||
|
url(r'^asset-tag/(?P<pk>[0-9]+)/$', views.AssetTagDetailView.as_view(), name='asset-tag-detail'),
|
||||||
|
url(r'^asset-tag/(?P<pk>[0-9]+)/update/$', views.AssetTagUpdateView.as_view(), name='asset-tag-update'),
|
||||||
|
url(r'^asset-tag/(?P<pk>[0-9]+)/delete/$', views.AssetTagDeleteView.as_view(), name='asset-tag-delete'),
|
||||||
|
|
||||||
|
# Resource idc url
|
||||||
|
url(r'^idc/$', views.IDCListView.as_view(), name='idc-list'),
|
||||||
|
url(r'^idc/create/$', views.IDCCreateView.as_view(), name='idc-create'),
|
||||||
|
url(r'^idc/(?P<pk>[0-9]+)/$', views.IDCDetailView.as_view(), name='idc-detail'),
|
||||||
|
url(r'^idc/(?P<pk>[0-9]+)/update/', views.IDCUpdateView.as_view(), name='idc-update'),
|
||||||
|
url(r'^idc/(?P<pk>[0-9]+)/delete/$', views.IDCDeleteView.as_view(), name='idc-delete'),
|
||||||
|
url(r'^idc/(?P<pk>[0-9]+)/assets/$', views.IDCAssetsView.as_view(), name='idc-assets'),
|
||||||
|
|
||||||
|
# Resource admin user url
|
||||||
|
url(r'^admin-user/$', views.AdminUserListView.as_view(), name='admin-user-list'),
|
||||||
|
url(r'^admin-user/create/$', views.AdminUserCreateView.as_view(), name='admin-user-create'),
|
||||||
|
url(r'^admin-user/(?P<pk>[0-9]+)/$', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
|
||||||
|
url(r'^admin-user/(?P<pk>[0-9]+)/update/$', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
|
||||||
|
url(r'^admin-user/(?P<pk>[0-9]+)/delete/$', views.AdminUserDeleteView.as_view(), name='admin-user-delete'),
|
||||||
|
|
||||||
|
# Resource system user url
|
||||||
|
url(r'^system-user/$', views.SystemUserListView.as_view(), name='system-user-list'),
|
||||||
|
url(r'^system-user/create/$', views.SystemUserCreateView.as_view(), name='system-user-create'),
|
||||||
|
url(r'^system-user/(?P<pk>[0-9]+)/$', views.SystemUserDetailView.as_view(), name='system-user-detail'),
|
||||||
|
url(r'^system-user/(?P<pk>[0-9]+)/update/$', views.SystemUserUpdateView.as_view(), name='system-user-update'),
|
||||||
|
url(r'^system-user/(?P<pk>[0-9]+)/delete/$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
|
||||||
|
url(r'^system-user/(?P<pk>[0-9]+)/asset/$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
|
||||||
|
# url(r'^system-user/(?P<pk>[0-9]+)/asset-group$', views.SystemUserAssetGroupView.as_view(),
|
||||||
|
# name='system-user-asset-group'),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
#
|
||||||
|
from rest_framework import serializers
|
||||||
|
from models import Tag
|
||||||
|
from django.views.generic.edit import CreateView
|
||||||
|
|
||||||
|
class CreateAssetTagsMiXin(CreateView):
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
tags_list = self.request.POST.getlist('tags')
|
||||||
|
kwargs = {
|
||||||
|
'initial': self.get_initial(),
|
||||||
|
'prefix': self.get_prefix(),
|
||||||
|
}
|
||||||
|
if self.request.method in ('POST', 'PUT'):
|
||||||
|
post_data = self.request.POST.copy()
|
||||||
|
if post_data.has_key('tags'):
|
||||||
|
post_data.pop('tags')
|
||||||
|
for t in tags_list:
|
||||||
|
try:
|
||||||
|
oTag = Tag.objects.get(pk=int(t))
|
||||||
|
except (Tag.DoesNotExist, UnicodeEncodeError):
|
||||||
|
oTag = Tag(name=t, created_by=self.request.user.username or 'Admin')
|
||||||
|
oTag.save()
|
||||||
|
post_data.update({'tags':oTag.pk})
|
||||||
|
else:
|
||||||
|
post_data.update({'tags':int(t)})
|
||||||
|
kwargs.update({
|
||||||
|
'data': post_data,
|
||||||
|
'files': self.request.FILES,
|
||||||
|
})
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
class UpdateAssetTagsMiXin(CreateAssetTagsMiXin):
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super(UpdateAssetTagsMiXin, self).get_form_kwargs()
|
||||||
|
if hasattr(self, 'object'):
|
||||||
|
kwargs.update({'instance': self.object})
|
||||||
|
return kwargs
|
|
@ -0,0 +1,855 @@
|
||||||
|
# coding:utf-8
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
import json
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.writer.excel import save_virtual_workbook
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.db import IntegrityError
|
||||||
|
from django.views.generic import TemplateView, ListView, View
|
||||||
|
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
|
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||||
|
from django.shortcuts import get_object_or_404, reverse, redirect
|
||||||
|
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
|
||||||
|
from django.views.decorators.csrf import csrf_protect, csrf_exempt
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from common.mixins import JSONResponseMixin
|
||||||
|
from common.utils import get_object_or_none
|
||||||
|
from .utils import CreateAssetTagsMiXin, UpdateAssetTagsMiXin
|
||||||
|
from . import forms
|
||||||
|
from .models import Asset, AssetGroup, AdminUser, IDC, SystemUser, Tag
|
||||||
|
from .hands import AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
|
class AssetListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'assets/asset_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': 'Assets',
|
||||||
|
'action': 'asset list',
|
||||||
|
'groups': AssetGroup.objects.all(),
|
||||||
|
'system_users': SystemUser.objects.all(),
|
||||||
|
'tag_list': [(i.id, i.name, i.assets.all().count())for i in Tag.objects.all().order_by('name')],
|
||||||
|
'tags': Tag.objects.all().order_by('name')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
|
||||||
|
model = Asset
|
||||||
|
tag_type = 'asset'
|
||||||
|
form_class = forms.AssetCreateForm
|
||||||
|
template_name = 'assets/asset_create.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
asset = form.save()
|
||||||
|
asset.created_by = self.request.user.username or 'Admin'
|
||||||
|
asset.save()
|
||||||
|
return super(AssetCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
if form.errors.get('__all__'):
|
||||||
|
form.errors['all'] = form.errors.get('__all__')
|
||||||
|
return super(AssetCreateView, self).form_invalid(form)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': 'Assets',
|
||||||
|
'action': 'Create asset',
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetModalCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, ListView):
|
||||||
|
model = Asset
|
||||||
|
form_class = forms.AssetCreateForm
|
||||||
|
template_name = 'assets/asset_modal_update.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
self.queryset = super(AssetModalCreateView,self).get_queryset()
|
||||||
|
self.s = self.request.GET.get('plain_id_lists')
|
||||||
|
if "," in str(self.s):
|
||||||
|
self.plain_id_lists = [int(x) for x in self.s.split(',')]
|
||||||
|
else:
|
||||||
|
self.plain_id_lists = [self.s]
|
||||||
|
return self.queryset
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
asset_on_list = Asset.objects.filter(id__in = self.plain_id_lists)
|
||||||
|
context = {
|
||||||
|
'app': 'Assets',
|
||||||
|
'action': 'Bulk Update asset',
|
||||||
|
'assets_on_list': asset_on_list,
|
||||||
|
'assets_count': len(asset_on_list),
|
||||||
|
'plain_id_lists':self.s,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetModalCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetUpdateView(AdminUserRequiredMixin, UpdateAssetTagsMiXin, UpdateView):
|
||||||
|
model = Asset
|
||||||
|
form_class = forms.AssetCreateForm
|
||||||
|
template_name = 'assets/asset_update.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
new_form = ''
|
||||||
|
assets_ids = ''
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
default_keys = [
|
||||||
|
'csrfmiddlewaretoken',
|
||||||
|
'assets_ids',
|
||||||
|
'ip',
|
||||||
|
'number',
|
||||||
|
'hostname',
|
||||||
|
'system_users',
|
||||||
|
'admin_user',
|
||||||
|
]
|
||||||
|
self.assets_ids = self.request.POST.getlist('assets_ids')
|
||||||
|
self.new_form = self.request.POST.copy()
|
||||||
|
for i in default_keys:
|
||||||
|
if self.new_form.has_key(i):
|
||||||
|
self.new_form.pop(i)
|
||||||
|
|
||||||
|
return super(AssetUpdateView, self).post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': 'Assets',
|
||||||
|
'action': 'Update asset',
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
print(form.errors)
|
||||||
|
return super(AssetUpdateView, self).form_invalid(form)
|
||||||
|
|
||||||
|
# def form_valid(self, form):
|
||||||
|
# asset = form.save(commit=False)
|
||||||
|
#
|
||||||
|
# def prn_obj_key(obj_form):
|
||||||
|
# return obj_form.clean().keys()
|
||||||
|
#
|
||||||
|
# for i in prn_obj_key(form):
|
||||||
|
# if i not in self.new_form.keys():
|
||||||
|
# print i
|
||||||
|
|
||||||
|
#delattr(asset, '"%s" % i')
|
||||||
|
#del asset.i
|
||||||
|
asset.save()
|
||||||
|
asset.id = 27
|
||||||
|
# asset.created_by = self.request.user.username or 'Admin'
|
||||||
|
asset.save()
|
||||||
|
asset.id = 28
|
||||||
|
asset.save()
|
||||||
|
return super(AssetUpdateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetDeleteView(DeleteView):
|
||||||
|
model = Asset
|
||||||
|
template_name = 'assets/delete_confirm.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
|
||||||
|
|
||||||
|
class AssetDetailView(DetailView):
|
||||||
|
model = Asset
|
||||||
|
context_object_name = 'asset'
|
||||||
|
template_name = 'assets/asset_detail.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
asset_groups = self.object.groups.all()
|
||||||
|
system_users = self.object.system_users.all()
|
||||||
|
context = {
|
||||||
|
'app': 'Assets',
|
||||||
|
'action': 'Asset detail',
|
||||||
|
'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all()
|
||||||
|
if asset_group not in asset_groups],
|
||||||
|
'asset_groups': asset_groups,
|
||||||
|
'system_users_remain': [system_user for system_user in SystemUser.objects.all()
|
||||||
|
if system_user not in system_users],
|
||||||
|
'system_users': system_users,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetDetailView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetModalListView(AdminUserRequiredMixin, ListView):
|
||||||
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
|
model = Asset
|
||||||
|
context_object_name = 'asset_modal_list'
|
||||||
|
template_name = 'assets/asset_modal_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
group_id = self.request.GET.get('group_id')
|
||||||
|
tag_id = self.request.GET.get('tag_id')
|
||||||
|
plain_id_lists = self.request.GET.get('plain_id_lists')
|
||||||
|
self.s = self.request.GET.get('plain_id_lists')
|
||||||
|
if "," in str(self.s):
|
||||||
|
self.plain_id_lists = [int(x) for x in self.s.split(',')]
|
||||||
|
else:
|
||||||
|
self.plain_id_lists = [self.s]
|
||||||
|
|
||||||
|
if plain_id_lists:
|
||||||
|
if "," in str(self.s):
|
||||||
|
plain_id_lists = [int(x) for x in self.s.split(',')]
|
||||||
|
else:
|
||||||
|
plain_id_lists = [int(self.s)]
|
||||||
|
context = {
|
||||||
|
'all_assets' :plain_id_lists
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
if group_id:
|
||||||
|
group = AssetGroup.objects.get(id=group_id)
|
||||||
|
context = {
|
||||||
|
'all_assets': [x.id for x in group.assets.all()]
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
if tag_id:
|
||||||
|
tag = Tag.objects.get(id=tag_id)
|
||||||
|
context = {
|
||||||
|
'all_assets': [x.id for x in tag.asset_set.all()]
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetModalListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
|
model = AssetGroup
|
||||||
|
form_class = forms.AssetGroupForm
|
||||||
|
template_name = 'assets/asset_group_create.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-group-list')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Create asset group'),
|
||||||
|
'assets_count': 0,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetGroupCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
asset_group = form.save()
|
||||||
|
assets_id_list = self.request.POST.getlist('assets', [])
|
||||||
|
assets = [get_object_or_404(Asset, id=int(asset_id))
|
||||||
|
for asset_id in assets_id_list]
|
||||||
|
asset_group.created_by = self.request.user.username or 'Admin'
|
||||||
|
asset_group.assets.add(*tuple(assets))
|
||||||
|
asset_group.save()
|
||||||
|
return super(AssetGroupCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'assets/asset_group_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Asset group list'),
|
||||||
|
'assets': Asset.objects.all(),
|
||||||
|
'system_users': SystemUser.objects.all(),
|
||||||
|
'keyword': self.request.GET.get('keyword', '')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetGroupListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
|
||||||
|
model = AssetGroup
|
||||||
|
template_name = 'assets/asset_group_detail.html'
|
||||||
|
context_object_name = 'asset_group'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
assets_remain = Asset.objects.exclude(id__in=self.object.assets.all())
|
||||||
|
system_users = self.object.system_users.all()
|
||||||
|
system_users_remain = SystemUser.objects.exclude(id__in=system_users)
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Asset group detail'),
|
||||||
|
'assets_remain': assets_remain,
|
||||||
|
'assets': [asset for asset in Asset.objects.all()
|
||||||
|
if asset not in assets_remain],
|
||||||
|
'system_users': system_users,
|
||||||
|
'system_users_remain': system_users_remain,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetGroupDetailView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
model = AssetGroup
|
||||||
|
form_class = forms.AssetGroupForm
|
||||||
|
template_name = 'assets/asset_group_create.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-group-list')
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object(queryset=AssetGroup.objects.all())
|
||||||
|
return super(AssetGroupUpdateView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
assets_all = self.object.assets.all()
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Create asset group'),
|
||||||
|
'assets_on_list': assets_all,
|
||||||
|
'assets_count': len(assets_all),
|
||||||
|
'group_id':self.object.id,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetGroupUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||||
|
template_name = 'assets/delete_confirm.html'
|
||||||
|
model = AssetGroup
|
||||||
|
success_url = reverse_lazy('assets:asset-group-list')
|
||||||
|
|
||||||
|
|
||||||
|
class IDCListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'assets/idc_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('IDC list'),
|
||||||
|
# 'keyword': self.request.GET.get('keyword', '')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(IDCListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class IDCCreateView(AdminUserRequiredMixin, CreateView):
|
||||||
|
model = IDC
|
||||||
|
form_class = forms.IDCForm
|
||||||
|
template_name = 'assets/idc_create_update.html'
|
||||||
|
success_url = reverse_lazy('assets:idc-list')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('assets'),
|
||||||
|
'action': _('Create IDC'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(IDCCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
idc = form.save(commit=False)
|
||||||
|
idc.created_by = self.request.user.username or 'System'
|
||||||
|
idc.save()
|
||||||
|
return super(IDCCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
model = IDC
|
||||||
|
form_class = forms.IDCForm
|
||||||
|
template_name = 'assets/idc_create_update.html'
|
||||||
|
context_object_name = 'idc'
|
||||||
|
success_url = reverse_lazy('assets:idc-list')
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
idc = form.save(commit=False)
|
||||||
|
idc.save()
|
||||||
|
return super(IDCUpdateView, self).form_valid(form)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('assets'),
|
||||||
|
'action': _('Update IDC'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(IDCUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class IDCDetailView(AdminUserRequiredMixin, DetailView):
|
||||||
|
model = IDC
|
||||||
|
template_name = 'assets/idc_detail.html'
|
||||||
|
context_object_name = 'idc'
|
||||||
|
|
||||||
|
|
||||||
|
class IDCAssetsView(AdminUserRequiredMixin, DetailView):
|
||||||
|
model = IDC
|
||||||
|
template_name = 'assets/idc_assets.html'
|
||||||
|
context_object_name = 'idc'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
assets_remain = Asset.objects.exclude(id__in=self.object.assets.all())
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Asset detail'),
|
||||||
|
'groups': AssetGroup.objects.all(),
|
||||||
|
'system_users': SystemUser.objects.all(),
|
||||||
|
'tags': Tag.objects.all(),
|
||||||
|
'assets_remain': assets_remain,
|
||||||
|
'assets': [asset for asset in Asset.objects.all() if asset not in assets_remain],
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(IDCAssetsView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class IDCDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||||
|
model = IDC
|
||||||
|
template_name = 'assets/delete_confirm.html'
|
||||||
|
success_url = reverse_lazy('assets:idc-list')
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
model = AdminUser
|
||||||
|
template_name = 'assets/admin_user_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Admin user list'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AdminUserListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserCreateView(AdminUserRequiredMixin,
|
||||||
|
SuccessMessageMixin,
|
||||||
|
CreateView):
|
||||||
|
model = AdminUser
|
||||||
|
form_class = forms.AdminUserForm
|
||||||
|
template_name = 'assets/admin_user_create_update.html'
|
||||||
|
success_url = reverse_lazy('assets:admin-user-list')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': 'assets',
|
||||||
|
'action': 'Create admin user'
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AdminUserCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get_success_message(self, cleaned_data):
|
||||||
|
success_message = _(
|
||||||
|
'Create admin user <a href="%s">%s</a> successfully.' % (
|
||||||
|
reverse_lazy('assets:admin-user-detail',
|
||||||
|
kwargs={'pk': self.object.pk}),
|
||||||
|
self.object.name,
|
||||||
|
))
|
||||||
|
return success_message
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
return super(AdminUserCreateView, self).form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
model = AdminUser
|
||||||
|
form_class = forms.AdminUserForm
|
||||||
|
template_name = 'assets/admin_user_create_update.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': 'assets',
|
||||||
|
'action': 'Update admin user'
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AdminUserUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
success_url = reverse_lazy('assets:admin-user-detail',
|
||||||
|
kwargs={'pk': self.object.pk})
|
||||||
|
return success_url
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
|
||||||
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
|
template_name = 'assets/admin_user_detail.html'
|
||||||
|
context_object_name = 'admin_user'
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object(queryset=AdminUser.objects.all())
|
||||||
|
return super(AdminUserDetailView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
# Todo: queryset default order by connectivity, need ops support
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.object.assets.all()
|
||||||
|
|
||||||
|
# def get_asset_groups(self):
|
||||||
|
# return self.object.asset_groups.all()
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
asset_groups = AssetGroup.objects.all()
|
||||||
|
assets = self.get_queryset()
|
||||||
|
context = {
|
||||||
|
'app': 'assets',
|
||||||
|
'action': 'Admin user detail',
|
||||||
|
'assets_remain': [asset for asset in Asset.objects.all() if asset not in assets],
|
||||||
|
'asset_groups': asset_groups,
|
||||||
|
# 'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all()
|
||||||
|
# if asset_group not in asset_groups]
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AdminUserDetailView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||||
|
model = AdminUser
|
||||||
|
template_name = 'assets/delete_confirm.html'
|
||||||
|
success_url = reverse_lazy('assets:admin-user-list')
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserListView(AdminUserRequiredMixin, TemplateView):
|
||||||
|
template_name = 'assets/system_user_list.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('System user list'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(SystemUserListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||||
|
model = SystemUser
|
||||||
|
form_class = forms.SystemUserForm
|
||||||
|
template_name = 'assets/system_user_create_update.html'
|
||||||
|
success_url = reverse_lazy('assets:system-user-list')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Create system user'),
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(SystemUserCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
print(form.errors)
|
||||||
|
return super(SystemUserCreateView, self).form_invalid(form)
|
||||||
|
|
||||||
|
def get_success_message(self, cleaned_data):
|
||||||
|
success_message = _('Create system user <a href="%s">%s</a> successfully.' %
|
||||||
|
(
|
||||||
|
reverse_lazy('assets:system-user-detail', kwargs={'pk': self.object.pk}),
|
||||||
|
self.object.name,
|
||||||
|
))
|
||||||
|
|
||||||
|
return success_message
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
model = SystemUser
|
||||||
|
form_class = forms.SystemUserForm
|
||||||
|
template_name = 'assets/system_user_create_update.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('Update system user')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(SystemUserUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
success_url = reverse_lazy('assets:system-user-detail', kwargs={'pk': self.object.pk})
|
||||||
|
return success_url
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
|
||||||
|
template_name = 'assets/system_user_detail.html'
|
||||||
|
context_object_name = 'system_user'
|
||||||
|
model = SystemUser
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Assets'),
|
||||||
|
'action': _('System user detail')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(SystemUserDetailView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||||
|
model = SystemUser
|
||||||
|
template_name = 'assets/delete_confirm.html'
|
||||||
|
success_url = reverse_lazy('assets:system-user-list')
|
||||||
|
|
||||||
|
|
||||||
|
class SystemUserAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
|
||||||
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
|
template_name = 'assets/system_user_asset.html'
|
||||||
|
context_object_name = 'system_user'
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object(queryset=SystemUser.objects.all())
|
||||||
|
return super(SystemUserAssetView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_asset_groups(self):
|
||||||
|
return self.object.asset_groups.all()
|
||||||
|
|
||||||
|
# Todo: queryset default order by connectivity, need ops support
|
||||||
|
def get_queryset(self):
|
||||||
|
return list(self.object.get_assets())
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
asset_groups = self.get_asset_groups()
|
||||||
|
assets = self.get_queryset()
|
||||||
|
context = {
|
||||||
|
'app': 'assets',
|
||||||
|
'action': 'System user asset',
|
||||||
|
'assets_remain': [asset for asset in Asset.objects.all() if asset not in assets],
|
||||||
|
'asset_groups': asset_groups,
|
||||||
|
'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all()
|
||||||
|
if asset_group not in asset_groups]
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(SystemUserAssetView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TagView(ListView):
|
||||||
|
context_object_name = 'asset_list'
|
||||||
|
template_name = 'assets/asset_list.html'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
asset_list = Asset.objects.filter(tags=self.kwargs['tag_id'])
|
||||||
|
return asset_list
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
kwargs['app'] = 'Assets'
|
||||||
|
kwargs['action']='asset list'
|
||||||
|
kwargs['tag_list'] = [(i.id,i.name,i.asset_set.all().count() )for i in Tag.objects.all().order_by('name')]
|
||||||
|
kwargs['tag_id'] = self.kwargs['tag_id']
|
||||||
|
return super(TagView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TagsListView(AdminUserRequiredMixin, ListView):
|
||||||
|
model = Tag
|
||||||
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
|
context_object_name = 'asset_tags_list'
|
||||||
|
template_name = 'assets/asset_tags_list.html'
|
||||||
|
ordering = '-id'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Tag'),
|
||||||
|
'action': _('Asset Tags list'),
|
||||||
|
'keyword': self.request.GET.get('keyword', '')
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(TagsListView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetTagCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
|
||||||
|
model = Tag
|
||||||
|
form_class = forms.AssetTagForm
|
||||||
|
template_name = 'assets/asset_tag_create.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-tag-list')
|
||||||
|
#ordering = '-id'
|
||||||
|
|
||||||
|
# Todo: Asset group create template select assets so hard, need be resolve next
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {
|
||||||
|
'app': _('Tag'),
|
||||||
|
'action': _('Asset Tags list'),
|
||||||
|
'assets_count': 0,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetTagCreateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
asset_tag = form.save()
|
||||||
|
assets_id_list = self.request.POST.getlist('assets', [])
|
||||||
|
assets = [get_object_or_404(Asset, id=int(asset_id)) for asset_id in assets_id_list]
|
||||||
|
asset_tag.created_by = self.request.user.username or 'Admin'
|
||||||
|
asset_tag.assets.add(*tuple(assets))
|
||||||
|
asset_tag.save()
|
||||||
|
return super(AssetTagCreateView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetTagDetailView(SingleObjectMixin, AdminUserRequiredMixin, ListView):
|
||||||
|
template_name = 'assets/asset_tag_detail.html'
|
||||||
|
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object(queryset=Tag.objects.all())
|
||||||
|
return super(AssetTagDetailView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return self.object.assets.all()
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
assets_remain = Asset.objects.exclude(id__in=self.object.assets.all())
|
||||||
|
context = {
|
||||||
|
'app': _('Tag'),
|
||||||
|
'action': _('Asset Tags detail'),
|
||||||
|
'asset_tag': self.object,
|
||||||
|
'assets_remain': assets_remain,
|
||||||
|
'assets': [asset for asset in Asset.objects.all() if asset not in assets_remain]
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetTagDetailView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetTagUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||||
|
model = Tag
|
||||||
|
form_class = forms.AssetTagForm
|
||||||
|
template_name = 'assets/asset_tag_create.html'
|
||||||
|
success_url = reverse_lazy('assets:asset-tag-list')
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object(queryset=Tag.objects.all())
|
||||||
|
return super(AssetTagUpdateView, self).get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
assets_all = self.object.assets.all()
|
||||||
|
context = {
|
||||||
|
'app': _('Tag'),
|
||||||
|
'action': _('Asset Tags detail'),
|
||||||
|
'assets_count': len(assets_all),
|
||||||
|
'assets_on_list': assets_all,
|
||||||
|
'tag_id':self.object.id,
|
||||||
|
}
|
||||||
|
kwargs.update(context)
|
||||||
|
return super(AssetTagUpdateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class AssetTagDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||||
|
template_name = 'assets/delete_confirm.html'
|
||||||
|
model = Tag
|
||||||
|
success_url = reverse_lazy('assets:asset-tag-list')
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class AssetExportView(View):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_asset_attr(asset, attr):
|
||||||
|
if attr in ['admin_user', 'idc']:
|
||||||
|
return getattr(asset, attr).name
|
||||||
|
elif attr in ['status', 'type', 'env']:
|
||||||
|
return getattr(asset, 'get_{}_display'.format(attr))()
|
||||||
|
else:
|
||||||
|
return getattr(asset, attr)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
spm = request.GET.get('spm', '')
|
||||||
|
assets_id = cache.get(spm)
|
||||||
|
if not assets_id and not isinstance(assets_id, list):
|
||||||
|
return HttpResponse('May be expired', status=404)
|
||||||
|
|
||||||
|
assets = Asset.objects.filter(id__in=assets_id)
|
||||||
|
wb = Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = 'Asset'
|
||||||
|
header = ['hostname', 'ip', 'port', 'admin_user', 'idc', 'cpu', 'memory', 'disk',
|
||||||
|
'mac_address', 'other_ip', 'remote_card_ip', 'os', 'cabinet_no',
|
||||||
|
'cabinet_pos', 'number', 'status', 'type', 'env', 'sn', 'comment']
|
||||||
|
ws.append(header)
|
||||||
|
|
||||||
|
for asset in assets:
|
||||||
|
ws.append([self.get_asset_attr(asset, attr) for attr in header])
|
||||||
|
|
||||||
|
filename = 'assets-{}.xlsx'.format(timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
|
||||||
|
response = HttpResponse(save_virtual_workbook(wb), content_type='applications/vnd.ms-excel')
|
||||||
|
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||||
|
return response
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
assets_id = json.loads(request.body).get('assets_id', [])
|
||||||
|
print(assets_id)
|
||||||
|
except ValueError:
|
||||||
|
return HttpResponse('Json object not valid', status=400)
|
||||||
|
spm = uuid.uuid4().get_hex()
|
||||||
|
cache.set(spm, assets_id, 300)
|
||||||
|
url = reverse_lazy('assets:asset-export') + '?spm=%s' % spm
|
||||||
|
return JsonResponse({'redirect': url})
|
||||||
|
|
||||||
|
|
||||||
|
class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
||||||
|
form_class = forms.FileForm
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
try:
|
||||||
|
wb = load_workbook(form.cleaned_data['file'])
|
||||||
|
ws = wb.get_active_sheet()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
data = {'valid': False, 'msg': 'Not a valid Excel file'}
|
||||||
|
return self.render_json_response(data)
|
||||||
|
|
||||||
|
rows = ws.rows
|
||||||
|
header_all = ['hostname', 'ip', 'port', 'admin_user', 'idc', 'cpu', 'memory', 'disk',
|
||||||
|
'mac_address', 'other_ip', 'remote_card_ip', 'os', 'cabinet_no',
|
||||||
|
'cabinet_pos', 'number', 'status', 'type', 'env', 'sn', 'comment']
|
||||||
|
header_min = ['hostname', 'ip', 'port', 'admin_user', 'comment']
|
||||||
|
header = [col.value for col in next(rows)]
|
||||||
|
if not set(header).issubset(set(header_all)) and not set(header).issuperset(set(header_min)):
|
||||||
|
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
|
||||||
|
return self.render_json_response(data)
|
||||||
|
|
||||||
|
created = []
|
||||||
|
updated = []
|
||||||
|
failed = []
|
||||||
|
for row in rows:
|
||||||
|
asset_dict = dict(zip(header, [col.value for col in row]))
|
||||||
|
if asset_dict.get('admin_user', None):
|
||||||
|
admin_user = get_object_or_none(AdminUser, name=asset_dict['admin_user'])
|
||||||
|
asset_dict['admin_user'] = admin_user
|
||||||
|
|
||||||
|
if asset_dict.get('idc'):
|
||||||
|
idc = get_object_or_none(IDC, name=asset_dict['idc'])
|
||||||
|
asset_dict['idc'] = idc
|
||||||
|
|
||||||
|
if asset_dict.get('type'):
|
||||||
|
asset_display_type_map = dict(zip(dict(Asset.TYPE_CHOICES).values(), dict(Asset.TYPE_CHOICES).keys()))
|
||||||
|
asset_type = asset_display_type_map.get(asset_dict['type'], 'Server')
|
||||||
|
asset_dict['type'] = asset_type
|
||||||
|
|
||||||
|
if asset_dict.get('status'):
|
||||||
|
asset_display_status_map = dict(zip(dict(Asset.STATUS_CHOICES).values(),
|
||||||
|
dict(Asset.STATUS_CHOICES).keys()))
|
||||||
|
asset_status = asset_display_status_map.get(asset_dict['status'], 'In use')
|
||||||
|
asset_dict['status'] = asset_status
|
||||||
|
|
||||||
|
if asset_dict.get('env'):
|
||||||
|
asset_display_env_map = dict(zip(dict(Asset.ENV_CHOICES).values(),
|
||||||
|
dict(Asset.ENV_CHOICES).keys()))
|
||||||
|
asset_env = asset_display_env_map.get(asset_dict['env'], 'Prod')
|
||||||
|
asset_dict['env'] = asset_env
|
||||||
|
|
||||||
|
try:
|
||||||
|
Asset.objects.create(**asset_dict)
|
||||||
|
created.append(asset_dict['ip'])
|
||||||
|
except IntegrityError as e:
|
||||||
|
asset = Asset.objects.filter(ip=asset_dict['ip'], port=asset_dict['port'])
|
||||||
|
if not asset:
|
||||||
|
failed.append(asset_dict['ip'])
|
||||||
|
continue
|
||||||
|
asset.update(**asset_dict)
|
||||||
|
updated.append(asset_dict['ip'])
|
||||||
|
except TypeError as e:
|
||||||
|
print(e)
|
||||||
|
failed.append(asset_dict['ip'])
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'created': created,
|
||||||
|
'created_info': 'Created {}'.format(len(created)),
|
||||||
|
'updated': updated,
|
||||||
|
'updated_info': 'Updated {}'.format(len(updated)),
|
||||||
|
'failed': failed,
|
||||||
|
'failed_info': 'Failed {}'.format(len(failed)),
|
||||||
|
'valid': True,
|
||||||
|
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(failed))
|
||||||
|
}
|
||||||
|
return self.render_json_response(data)
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from rest_framework import generics, viewsets
|
||||||
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
|
from audits.backends import command_store, record_store
|
||||||
|
from audits.backends.command.serializers import CommandLogSerializer
|
||||||
|
from audits.backends.record.serializers import RecordSerializer
|
||||||
|
from . import models, serializers
|
||||||
|
from .hands import IsSuperUserOrAppUser, IsAppUser
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyLogReceiveView(generics.CreateAPIView):
|
||||||
|
queryset = models.ProxyLog.objects.all()
|
||||||
|
serializer_class = serializers.ProxyLogSerializer
|
||||||
|
permission_classes = (IsAppUser,)
|
||||||
|
|
||||||
|
def get_serializer(self, *args, **kwargs):
|
||||||
|
kwargs['data']['terminal'] = self.request.user.terminal.name
|
||||||
|
return super(ProxyLogReceiveView, self).get_serializer(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyLogViewSet(viewsets.ModelViewSet):
|
||||||
|
"""User proxy to backend server need call this api.
|
||||||
|
|
||||||
|
params: {
|
||||||
|
"username": "",
|
||||||
|
"name": "",
|
||||||
|
"hostname": "",
|
||||||
|
"ip": "",
|
||||||
|
"terminal": "",
|
||||||
|
"login_type": "",
|
||||||
|
"system_user": "",
|
||||||
|
"was_failed": "",
|
||||||
|
"date_start": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = models.ProxyLog.objects.all()
|
||||||
|
serializer_class = serializers.ProxyLogSerializer
|
||||||
|
permission_classes = (IsSuperUserOrAppUser,)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLogViewSet(BulkModelViewSet):
|
||||||
|
"""接受app发送来的command log, 格式如下
|
||||||
|
{
|
||||||
|
"proxy_log_id": 23,
|
||||||
|
"user": "admin",
|
||||||
|
"asset": "localhost",
|
||||||
|
"system_user": "web",
|
||||||
|
"command_no": 1,
|
||||||
|
"command": "whoami",
|
||||||
|
"output": "d2hvbWFp", # base64.b64encode(s)
|
||||||
|
"timestamp": 1485238673.0
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
queryset = command_store.all()
|
||||||
|
serializer_class = CommandLogSerializer
|
||||||
|
permission_classes = (IsSuperUserOrAppUser,)
|
||||||
|
|
||||||
|
|
||||||
|
class RecordLogViewSet(BulkModelViewSet):
|
||||||
|
"""接受app发送来的record log, 格式如下
|
||||||
|
{
|
||||||
|
"proxy_log_id": 23,
|
||||||
|
"output": "d2hvbWFp", # base64.b64encode(s)
|
||||||
|
"timestamp": 1485238673.0
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
serializer_class = RecordSerializer
|
||||||
|
permission_classes = (IsSuperUserOrAppUser,)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
filter_kwargs = {}
|
||||||
|
proxy_log_id = self.request.query_params.get('proxy_log_id')
|
||||||
|
data_from_ts = self.request.query_params.get('date_from_ts')
|
||||||
|
if proxy_log_id:
|
||||||
|
filter_kwargs['proxy_log_id'] = proxy_log_id
|
||||||
|
if data_from_ts:
|
||||||
|
filter_kwargs['date_from_ts'] = data_from_ts
|
||||||
|
if filter_kwargs:
|
||||||
|
return record_store.filter(**filter_kwargs)
|
||||||
|
else:
|
||||||
|
return record_store.all()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AuditsConfig(AppConfig):
|
||||||
|
name = 'audits'
|
|
@ -0,0 +1,10 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# coding: utf-8
|
||||||
|
import abc
|
||||||
|
|
||||||
|
|
||||||
|
class CommandBase(object):
|
||||||
|
__metaclass__ = abc.ABCMeta
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def save(self, proxy_log_id, user, asset, system_user,
|
||||||
|
command_no, command, output, timestamp):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def filter(self, date_from_ts=None, date_to_ts=None, user='',
|
||||||
|
asset='', system_user='', command='', proxy_log_id=0):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
|
from .base import CommandBase
|
||||||
|
from audits.models import CommandLog
|
||||||
|
|
||||||
|
|
||||||
|
class CommandStore(CommandBase):
|
||||||
|
model = CommandLog
|
||||||
|
queryset = []
|
||||||
|
|
||||||
|
def save(self, proxy_log_id, user, asset, system_user,
|
||||||
|
command_no, command, output, timestamp):
|
||||||
|
self.model.objects.create(
|
||||||
|
proxy_log_id=proxy_log_id, user=user, asset=asset,
|
||||||
|
system_user=system_user, command_no=command_no,
|
||||||
|
command=command, output=output, timestamp=timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter(self, date_from_ts=None, date_to_ts=None, user='',
|
||||||
|
asset='', system_user='', command='', proxy_log_id=0):
|
||||||
|
filter_kwargs = {}
|
||||||
|
|
||||||
|
if date_from_ts:
|
||||||
|
filter_kwargs['timestamp__gte'] = date_from_ts
|
||||||
|
if date_to_ts:
|
||||||
|
filter_kwargs['timestamp__lte'] = date_to_ts
|
||||||
|
if user:
|
||||||
|
filter_kwargs['user'] = user
|
||||||
|
if asset:
|
||||||
|
filter_kwargs['asset'] = asset
|
||||||
|
if system_user:
|
||||||
|
filter_kwargs['system_user'] = system_user
|
||||||
|
if command:
|
||||||
|
filter_kwargs['command__icontains'] = command
|
||||||
|
if proxy_log_id:
|
||||||
|
filter_kwargs['proxy_log_id'] = proxy_log_id
|
||||||
|
|
||||||
|
if filter_kwargs:
|
||||||
|
self.queryset = self.model.objects.filter(**filter_kwargs)
|
||||||
|
return self.queryset
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
"""返回所有数据"""
|
||||||
|
return self.model.objects.iterator()
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
import base64
|
||||||
|
from rest_framework import serializers
|
||||||
|
from audits.models import CommandLog
|
||||||
|
from audits.backends import command_store
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLogSerializer(serializers.ModelSerializer):
|
||||||
|
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CommandLog
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
try:
|
||||||
|
output = validated_data['output']
|
||||||
|
validated_data['output'] = base64.b64decode(output)
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
return command_store.save(**dict(validated_data))
|
|
@ -0,0 +1,2 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# coding: utf-8
|
||||||
|
import abc
|
||||||
|
|
||||||
|
|
||||||
|
class RecordBase(object):
|
||||||
|
__metaclass__ = abc.ABCMeta
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def save(self, proxy_log_id, output, timestamp):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def filter(self, date_from_ts=None, proxy_log_id=None):
|
||||||
|
pass
|
|
@ -0,0 +1,31 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
|
from .base import RecordBase
|
||||||
|
from audits.models import RecordLog
|
||||||
|
|
||||||
|
|
||||||
|
class RecordStore(RecordBase):
|
||||||
|
model = RecordLog
|
||||||
|
queryset = []
|
||||||
|
|
||||||
|
def save(self, proxy_log_id, output, timestamp):
|
||||||
|
return self.model.objects.create(
|
||||||
|
proxy_log_id=proxy_log_id, output=output, timestamp=timestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
def filter(self, date_from_ts=None, proxy_log_id=''):
|
||||||
|
filter_kwargs = {}
|
||||||
|
|
||||||
|
if date_from_ts:
|
||||||
|
filter_kwargs['timestamp__gte'] = date_from_ts
|
||||||
|
if proxy_log_id:
|
||||||
|
filter_kwargs['proxy_log_id'] = proxy_log_id
|
||||||
|
|
||||||
|
if filter_kwargs:
|
||||||
|
self.queryset = self.model.objects.filter(**filter_kwargs)
|
||||||
|
return self.queryset
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
"""返回所有数据"""
|
||||||
|
return self.model.objects.all()
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
import base64
|
||||||
|
from rest_framework import serializers
|
||||||
|
from audits.models import RecordLog
|
||||||
|
from audits.backends import record_store
|
||||||
|
|
||||||
|
|
||||||
|
class RecordSerializer(serializers.ModelSerializer):
|
||||||
|
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
|
||||||
|
class Meta:
|
||||||
|
model = RecordLog
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
try:
|
||||||
|
output = validated_data['output']
|
||||||
|
validated_data['output'] = base64.b64decode(output)
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
return record_store.save(**dict(validated_data))
|
|
@ -0,0 +1,8 @@
|
||||||
|
# ~*~ 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
|
|
@ -0,0 +1,95 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class LoginLog(models.Model):
|
||||||
|
LOGIN_TYPE_CHOICE = (
|
||||||
|
('W', 'Web'),
|
||||||
|
('ST', 'SSH Terminal'),
|
||||||
|
('WT', 'Web Terminal')
|
||||||
|
)
|
||||||
|
|
||||||
|
username = models.CharField(max_length=20, verbose_name=_('Username'))
|
||||||
|
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
|
||||||
|
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2,
|
||||||
|
verbose_name=_('Login type'))
|
||||||
|
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
|
||||||
|
login_city = models.CharField(max_length=100, blank=True, null=True,
|
||||||
|
verbose_name=_('Login city'))
|
||||||
|
user_agent = models.CharField(max_length=100, blank=True, null=True,
|
||||||
|
verbose_name=_('User agent'))
|
||||||
|
date_login = models.DateTimeField(auto_now_add=True,
|
||||||
|
verbose_name=_('Date login'))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = 'login_log'
|
||||||
|
ordering = ['-date_login', 'username']
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyLog(models.Model):
|
||||||
|
LOGIN_TYPE_CHOICE = (
|
||||||
|
('ST', 'SSH Terminal'),
|
||||||
|
('WT', 'Web Terminal'),
|
||||||
|
)
|
||||||
|
|
||||||
|
user = models.CharField(max_length=32, verbose_name=_('User'))
|
||||||
|
asset = models.CharField(max_length=32, verbose_name=_('Asset'))
|
||||||
|
system_user = models.CharField(max_length=32, verbose_name=_('System user'))
|
||||||
|
login_type = models.CharField(
|
||||||
|
choices=LOGIN_TYPE_CHOICE, max_length=2, blank=True,
|
||||||
|
null=True, verbose_name=_('Login type'))
|
||||||
|
terminal = models.CharField(
|
||||||
|
max_length=32, blank=True, null=True, verbose_name=_('Terminal'))
|
||||||
|
is_failed = models.BooleanField(
|
||||||
|
default=False, verbose_name=_('Did connect failed'))
|
||||||
|
is_finished = models.BooleanField(
|
||||||
|
default=False, verbose_name=_('Is finished'))
|
||||||
|
date_start = models.DateTimeField(
|
||||||
|
auto_created=True, verbose_name=_('Date start'))
|
||||||
|
date_finished = models.DateTimeField(
|
||||||
|
null=True, verbose_name=_('Date finished'))
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return '%s-%s-%s' % (self.user, self.asset, self.system_user)
|
||||||
|
|
||||||
|
def commands(self):
|
||||||
|
from audits.backends import command_store
|
||||||
|
return command_store.filter(proxy_log_id=self.id)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-date_start', 'user']
|
||||||
|
|
||||||
|
|
||||||
|
class CommandLog(models.Model):
|
||||||
|
proxy_log_id = models.IntegerField(db_index=True)
|
||||||
|
user = models.CharField(max_length=48, db_index=True)
|
||||||
|
asset = models.CharField(max_length=128, db_index=True)
|
||||||
|
system_user = models.CharField(max_length=48, db_index=True)
|
||||||
|
command_no = models.IntegerField()
|
||||||
|
command = models.CharField(max_length=1000, blank=True, db_index=True)
|
||||||
|
output = models.TextField(blank=True)
|
||||||
|
timestamp = models.FloatField(db_index=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return '%s: %s' % (self.id, self.command)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['command_no', 'command']
|
||||||
|
|
||||||
|
|
||||||
|
class RecordLog(models.Model):
|
||||||
|
proxy_log_id = models.IntegerField(db_index=True)
|
||||||
|
output = models.TextField(verbose_name=_('Output'))
|
||||||
|
timestamp = models.FloatField(db_index=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return 'Record: %s' % self.proxy_log_id
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['timestamp']
|
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from common.utils import timesince
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyLogSerializer(serializers.ModelSerializer):
|
||||||
|
time = serializers.SerializerMethodField()
|
||||||
|
command_length = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ProxyLog
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_time(obj):
|
||||||
|
if not obj.is_finished:
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
return timesince(obj.date_start, since=obj.date_finished)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_command_length(obj):
|
||||||
|
return 2
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
#
|
||||||
|
|
||||||
|
from celery import shared_task
|
||||||
|
from .utils import write_login_log
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def write_login_log_async(*args, **kwargs):
|
||||||
|
write_login_log(*args, **kwargs)
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% block content_left_head %}
|
||||||
|
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
|
||||||
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
#search_btn {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_search %}
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right form-inline">
|
||||||
|
<div class="form-group" id="date">
|
||||||
|
<div class="input-daterange input-group" id="datepicker">
|
||||||
|
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
|
||||||
|
<span class="input-group-addon">to</span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="user">
|
||||||
|
<option value="">{% trans 'User' %}</option>
|
||||||
|
{% for u in user_list %}
|
||||||
|
<option value="{{ u.username }}" {% if user == u.username %} selected {% endif %}>{{ u.username }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="asset">
|
||||||
|
<option value="">{% trans 'Asset' %}</option>
|
||||||
|
{% for a in asset_list %}
|
||||||
|
<option value="{{ a.ip }}" {% if asset == a.ip %} selected {% endif %}>{{ a.ip }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="system_user">
|
||||||
|
<option value="">{% trans 'System user' %}</option>
|
||||||
|
{% for s in system_user_list %}
|
||||||
|
<option value="{{ s.username }}" {% if s.username == system_user %} selected {% endif %}>{{ s.username }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" name="command" placeholder="Command" value="{{ command }}">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
搜索
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
{% block table_container %}
|
||||||
|
<table class="footable table table-stripped toggle-arrow-tiny" data-page="false">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-toggle="true">ID</th>
|
||||||
|
<th>Command</th>
|
||||||
|
<th>Username</th>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>System user</th>
|
||||||
|
<th>Proxy log</th>
|
||||||
|
<th>Datetime</th>
|
||||||
|
<th data-hide="all">Output</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for command in command_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ command.id }}</td>
|
||||||
|
<td>{{ command.command }}</td>
|
||||||
|
<td>{{ command.user }}</td>
|
||||||
|
<td>{{ command.asset }}</td>
|
||||||
|
<td>{{ command.system_user }}</td>
|
||||||
|
<td><a href="{% url 'audits:proxy-log-detail' pk=command.proxy_log_id %}">{{ command.proxy_log_id}}</a></td>
|
||||||
|
<td>{{ command.timestamp|ts_to_date }}</td>
|
||||||
|
<td><pre style="border: none; background: none">{{ command.output|to_html|safe }}</pre></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
|
||||||
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.footable').footable();
|
||||||
|
$('.select2').select2();
|
||||||
|
$('#date .input-daterange').datepicker({
|
||||||
|
dateFormat: 'mm/dd/yy',
|
||||||
|
keyboardNavigation: false,
|
||||||
|
forceParse: false,
|
||||||
|
autoclose: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% load common_tags %}
|
||||||
|
{% block content_left_head %}
|
||||||
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
#search_btn {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block table_search %}
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right form-inline">
|
||||||
|
<div class="form-group" id="date">
|
||||||
|
<div class="input-daterange input-group" id="datepicker">
|
||||||
|
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
|
||||||
|
<span class="input-group-addon">to</span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="username">
|
||||||
|
<option value="">{% trans 'Select user' %}</option>
|
||||||
|
{% for user in user_list %}
|
||||||
|
<option value="{{ user.username }}" {% if user.username == username %} selected {% endif %}>{{ user.username }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" name="keyword" placeholder="Search" value="{{ keyword }}">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
搜索
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_head %}
|
||||||
|
<th class="text-center">{% trans 'ID' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Username' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Name' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Type' %}</th>
|
||||||
|
<th class="text-center">{% trans 'UA' %}</th>
|
||||||
|
<th class="text-center">{% trans 'IP' %}</th>
|
||||||
|
<th class="text-center">{% trans 'City' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Date' %}</th>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_body %}
|
||||||
|
{% for login_log in login_log_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center">
|
||||||
|
{{ login_log.id }}
|
||||||
|
{# <a href="{% url 'audits:proxy-log-detail' pk=login_log.id %}">{{ login_log.id }}</a>#}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{ login_log.username }}</td>
|
||||||
|
<td class="text-center">{{ login_log.name }}</td>
|
||||||
|
<td class="text-center">{{ login_log.get_login_type_display }}</td>
|
||||||
|
{% if login_log.login_type == 'W' %}
|
||||||
|
<td class="text-center">
|
||||||
|
<span href="javascript:void(0);" data-toggle="tooltips" title="{{ login_log.user_agent }}">{{ login_log.user_agent | truncatechars:20 }}</span>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="text-center">{{ login_log.terminal }}</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="text-center">{{ login_log.login_ip }}</td>
|
||||||
|
<td class="text-center">{{ login_log.login_city }}</td>
|
||||||
|
<td class="text-center">{{ login_log.date_login }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('table').DataTable({
|
||||||
|
"searching": false,
|
||||||
|
"bInfo" : false,
|
||||||
|
"paging": false,
|
||||||
|
"order": []
|
||||||
|
});
|
||||||
|
$('#date .input-daterange').datepicker({
|
||||||
|
dateFormat: 'mm/dd/yy',
|
||||||
|
keyboardNavigation: false,
|
||||||
|
forceParse: false,
|
||||||
|
autoclose: true
|
||||||
|
});
|
||||||
|
$('.select2').select2();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta name="renderer" content="webkit">
|
||||||
|
{% include '_head_css_js.html' %}
|
||||||
|
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
|
||||||
|
<link href="{% static "css/plugins/footable/footable.core.css" %}" rel="stylesheet">
|
||||||
|
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
|
||||||
|
<script src="{% static 'js/plugins/sweetalert/sweetalert.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrapper wrapper-content animated fadeInRight">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="ibox-content">
|
||||||
|
<input type="text" class="form-control input-sm m-b-xs" id="filter"
|
||||||
|
placeholder="Search in table">
|
||||||
|
<table class="footable table table-stripped toggle-arrow-tiny" data-page-size="10" data-filter=#filter>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-toggle="true">ID</th>
|
||||||
|
<th>Command</th>
|
||||||
|
<th data-hide="all">Output</th>
|
||||||
|
<th>Datetime</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table_body">
|
||||||
|
{% for command in object_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ command.command_no }}</td>
|
||||||
|
<td>{{ command.command }}</td>
|
||||||
|
<td>{{ command.output_decode |safe }}</td>
|
||||||
|
<td>{{ command.datetime }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">
|
||||||
|
<ul class="pagination pull-right"></ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.footable').footable();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -0,0 +1,84 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block custom_head_css_js %}
|
||||||
|
<link href="{% static "css/plugins/footable/footable.core.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="panel-options">
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li class="active">
|
||||||
|
<a href="#" class="text-center"> {% trans 'Proxy log detail' %} </a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="col-sm-11" style="padding-left: 0;">
|
||||||
|
<div class="ibox float-e-margins">
|
||||||
|
<div class="ibox-title">
|
||||||
|
<span style="float: left">{% trans 'Command log list' %} <b>{{ user_object.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 id="command-log" class="footable table table-stripped toggle-arrow-tiny" data-page-size="10">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-toggle="true">ID</th>
|
||||||
|
<th>Command</th>
|
||||||
|
<th data-hide="all">Output</th>
|
||||||
|
<th>Datetime</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for command in object_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ command.command_no }}</td>
|
||||||
|
<td>{{ command.command }}</td>
|
||||||
|
<td><pre style="border: none;background: none">{{ command.output|to_html|safe}}</pre></td>
|
||||||
|
<td>{{ command.timestamp|ts_to_date}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<td colspan="5">
|
||||||
|
<ul class="pagination pull-right"></ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script src="{% static "js/plugins/footable/footable.all.min.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
$('.footable').footable();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,173 @@
|
||||||
|
{% extends '_base_list.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
{% block content_left_head %}
|
||||||
|
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
#search_btn {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block table_search %}
|
||||||
|
<form id="search_form" method="get" action="" class="pull-right form-inline">
|
||||||
|
<div class="form-group" id="date">
|
||||||
|
<div class="input-daterange input-group" id="datepicker">
|
||||||
|
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
|
||||||
|
<span class="input-group-addon">to</span>
|
||||||
|
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="username">
|
||||||
|
<option value="">{% trans 'User' %}</option>
|
||||||
|
{% for user in user_list %}
|
||||||
|
<option value="{{ user }}" {% if user == username %} selected {% endif %}>{{ user }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="ip">
|
||||||
|
<option value="">{% trans 'Asset' %}</option>
|
||||||
|
{% for asset in asset_list %}
|
||||||
|
<option value="{{ asset }}" {% if asset == ip %} selected {% endif %}>{{ asset }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="select2 form-control" name="system_user">
|
||||||
|
<option value="">{% trans 'System user' %}</option>
|
||||||
|
{% for su in system_user_list %}
|
||||||
|
<option value="{{ su }}" {% if su == system_user %} selected {% endif %}>{{ su }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control input-sm" name="keyword" placeholder="Keyword" value="{{ keyword }}">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
|
||||||
|
搜索
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_head %}
|
||||||
|
<th class="text-center"></th>
|
||||||
|
<th class="text-center">{% trans 'ID' %}</th>
|
||||||
|
<th class="text-center">{% trans 'User' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Asset' %}</th>
|
||||||
|
<th class="text-center">{% trans 'System user' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Terminal' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Command' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Success' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Finished' %}</th>
|
||||||
|
<th class="text-center">{% trans 'R/M' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Date start' %}</th>
|
||||||
|
<th class="text-center">{% trans 'Time' %}</th>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block table_body %}
|
||||||
|
{% for proxy_log in proxy_log_list %}
|
||||||
|
<tr class="gradeX">
|
||||||
|
<td class="text-center"><input type="checkbox" class="cbx-term" value="{{ proxy_log.id }}"></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="{% url 'audits:proxy-log-detail' pk=proxy_log.id %}">{{ proxy_log.id }}</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">{{ proxy_log.user }}</td>
|
||||||
|
<td class="text-center">{{ proxy_log.asset }}</td>
|
||||||
|
<td class="text-center">{{ proxy_log.system_user }}</td>
|
||||||
|
<td class="text-center">{{ proxy_log.terminal }}</td>
|
||||||
|
<td class="text-center">{{ proxy_log.commands.all|length}}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
{% if proxy_log.is_failed %}
|
||||||
|
<i class="fa fa-times text-danger"></i>
|
||||||
|
{% else %}
|
||||||
|
<i class="fa fa-check text-navy"></i>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% if proxy_log.is_finished %}
|
||||||
|
<td class="text-center">
|
||||||
|
<i class="fa fa-check text-navy"></i>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a><span class="text-navy"><i class="fa fa-play-circle"></i></span></a>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
<td class="text-center">
|
||||||
|
<a class="btn-term" value="{{ proxy_log.id }}"><i class="fa fa-times text-danger"></i></a>
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a><span class="text-danger"><i class="fa fa-eye"></i></span></a>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
<td class="text-center">{{ proxy_log.date_start }}</td>
|
||||||
|
<td class="text-center">{{ proxy_log.date_finished|timeuntil:proxy_log.date_start }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content_bottom_left %}
|
||||||
|
<div id="actions">
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
|
||||||
|
<option value="terminate">{% trans 'Terminate selected' %}</option>
|
||||||
|
</select>
|
||||||
|
<div class="input-group-btn pull-left" style="padding-left: 5px;">
|
||||||
|
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
|
||||||
|
{% trans 'Submit' %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block custom_foot_js %}
|
||||||
|
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
function terminateConnection(data) {
|
||||||
|
function success() {
|
||||||
|
window.setTimeout(function () {
|
||||||
|
window.location.reload()
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
var the_url = "{% url 'api-applications:terminate-connection' %}";
|
||||||
|
APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});
|
||||||
|
}
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('table').DataTable({
|
||||||
|
"searching": false,
|
||||||
|
"paging": false,
|
||||||
|
"bInfo" : false,
|
||||||
|
"order": []
|
||||||
|
});
|
||||||
|
$('.select2').select2();
|
||||||
|
$('#date .input-daterange').datepicker({
|
||||||
|
dateFormat: 'mm/dd/yy',
|
||||||
|
keyboardNavigation: false,
|
||||||
|
forceParse: false,
|
||||||
|
autoclose: true
|
||||||
|
});
|
||||||
|
}).on('click', '.btn-term', function () {
|
||||||
|
var $this = $(this);
|
||||||
|
var proxy_log_id = $this.attr('value');
|
||||||
|
var data = {
|
||||||
|
proxy_log_id: proxy_log_id
|
||||||
|
};
|
||||||
|
terminateConnection(data)
|
||||||
|
}).on('click', '#btn_bulk_update', function () {
|
||||||
|
var data = [];
|
||||||
|
$('.cbx-term:checked').each(function () {
|
||||||
|
data.push({proxy_log_id: $(this).attr('value')})
|
||||||
|
});
|
||||||
|
terminateConnection(data)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -0,0 +1 @@
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue