diff --git a/apps/assets/api.py b/apps/assets/api.py
index e3570e0f1..896aadc88 100644
--- a/apps/assets/api.py
+++ b/apps/assets/api.py
@@ -6,17 +6,18 @@ from rest_framework.views import APIView
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
from django.shortcuts import get_object_or_404
-from common.mixins import BulkDeleteApiMixin
+from common.mixins import IDInFilterMixin
from common.utils import get_object_or_none, signer
from .hands import IsSuperUserOrTerminalUser, IsSuperUser
from .models import AssetGroup, Asset, IDC, SystemUser, AdminUser
from . import serializers
-class AssetViewSet(viewsets.ModelViewSet):
+class AssetViewSet(IDInFilterMixin, viewsets.ModelViewSet):
"""API endpoint that allows Asset to be viewed or edited."""
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
+ filter_fields = ('id', 'ip', 'hostname')
def get_queryset(self):
queryset = super(AssetViewSet, self).get_queryset()
@@ -27,7 +28,6 @@ class AssetViewSet(viewsets.ModelViewSet):
if asset_group_id:
queryset = queryset.filter(groups__id=asset_group_id)
-
return queryset
@@ -71,7 +71,7 @@ class SystemUserViewSet(viewsets.ModelViewSet):
# return self.object.assets.all()
-class AssetListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView):
+class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsSuperUser,)
diff --git a/apps/assets/forms.py b/apps/assets/forms.py
index 09fcc9a24..aeb7062cb 100644
--- a/apps/assets/forms.py
+++ b/apps/assets/forms.py
@@ -309,4 +309,8 @@ class AssetTagForm(forms.ModelForm):
}
help_texts = {
'name': '* required',
- }
\ No newline at end of file
+ }
+
+
+class FileForm(forms.Form):
+ file = forms.FileField()
\ No newline at end of file
diff --git a/apps/assets/models.py b/apps/assets/models.py
index 1b13b8621..aa22a2f02 100644
--- a/apps/assets/models.py
+++ b/apps/assets/models.py
@@ -60,38 +60,6 @@ class IDC(models.Model):
continue
-class AssetExtend(models.Model):
- key = models.CharField(max_length=64, verbose_name=_('KEY'))
- value = models.CharField(max_length=64, verbose_name=_('VALUE'))
- created_by = models.CharField(max_length=32, blank=True, verbose_name=_("Created by"))
- date_created = models.DateTimeField(auto_now_add=True, null=True)
- comment = models.TextField(blank=True, verbose_name=_('Comment'))
-
- def __unicode__(self):
- return '%(key)s: %(value)s' % {'key': self.key, 'value': self.value}
-
- @classmethod
- def initial(cls):
- for k, v in (
- (_('status'), _('In use')),
- (_('status'), _('Out of use')),
- (_('type'), _('Server')),
- (_('type'), _('VM')),
- (_('type'), _('Switch')),
- (_('type'), _('Router')),
- (_('type'), _('Firewall')),
- (_('type'), _('Storage')),
- (_('env'), _('Production')),
- (_('env'), _('Development')),
- (_('env'), _('Testing')),
- ):
- cls.objects.create(key=k, value=v, created_by='System')
-
- class Meta:
- db_table = 'asset_extend'
- unique_together = ('key', 'value')
-
-
def private_key_validator(value):
if not validate_ssh_private_key(value):
raise ValidationError(
@@ -233,6 +201,10 @@ class SystemUser(models.Model):
def assets_amount(self):
return self.assets.count()
+ @property
+ def asset_group_amount(self):
+ return self.asset_groups.count()
+
class Meta:
db_table = 'system_user'
@@ -294,18 +266,29 @@ class AssetGroup(models.Model):
continue
-def get_default_extend(key, value):
- try:
- return AssetExtend.objects.get_or_create(key=key, value=value)[0]
- except:
- return None
-
-
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'))
@@ -326,15 +309,12 @@ class Asset(models.Model):
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.ForeignKey(AssetExtend, null=True, blank=True,
- related_name="status_asset", verbose_name=_('Asset status'),)
- # default=functools.partial(get_default_extend, 'status', 'In use'))
- type = models.ForeignKey(AssetExtend, blank=True,null=True, limit_choices_to={'key': 'type'},
- related_name="type_asset", verbose_name=_('Asset type'),)
- # default=functools.partial(get_default_extend, 'type','Server'))
- env = models.ForeignKey(AssetExtend, blank=True, null=True, limit_choices_to={'key': 'env'},
- related_name="env_asset", verbose_name=_('Asset environment'),)
- # default=functools.partial(get_default_extend, 'env', 'Production'))
+ 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'))
@@ -400,7 +380,7 @@ class Tag(models.Model):
def init_all_models():
- for cls in (AssetExtend, AssetGroup):
+ for cls in (AssetGroup,):
cls.initial()
@@ -410,5 +390,5 @@ def generate_fake():
def flush_all():
- for cls in (AssetGroup, AssetExtend, IDC, AdminUser, SystemUser, Asset):
+ for cls in (AssetGroup, IDC, AdminUser, SystemUser, Asset):
cls.objects.all().delete()
diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py
index 9613da621..7b13606e5 100644
--- a/apps/assets/serializers.py
+++ b/apps/assets/serializers.py
@@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
from rest_framework import viewsets, serializers,generics
-from .models import AssetGroup, Asset, IDC, AssetExtend, AdminUser, SystemUser
-from common.mixins import BulkDeleteApiMixin
+from .models import AssetGroup, Asset, IDC, AdminUser, SystemUser
+from common.mixins import IDInFilterMixin
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin
@@ -35,7 +35,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
def get_field_names(self, declared_fields, info):
fields = super(SystemUserSerializer, self).get_field_names(declared_fields, info)
- fields.append('assets_amount')
+ fields.extend(['assets_amount'])
return fields
@@ -43,7 +43,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
# system_users = SystemUserSerializer(many=True, read_only=True)
# admin_user = AdminUserSerializer(many=False, read_only=True)
hardware = serializers.SerializerMethodField()
- type_display = serializers.SerializerMethodField()
class Meta(object):
model = Asset
@@ -51,15 +50,16 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
@staticmethod
def get_hardware(obj):
- return '%s %s %s' % (obj.cpu, obj.memory, obj.disk)
-
- @staticmethod
- def get_type_display(obj):
- if obj.type:
- return obj.type.value
+ 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 = SystemUserSerializer(many=True, read_only=True)
diff --git a/apps/assets/templates/assets/_asset_import_modal.html b/apps/assets/templates/assets/_asset_import_modal.html
new file mode 100644
index 000000000..72d13f7a8
--- /dev/null
+++ b/apps/assets/templates/assets/_asset_import_modal.html
@@ -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 %}
+
{% trans "Download template or use export excel format" %}
+
+
+
+
+
+
+
+
+
+{% endblock %}
+{% block modal_confirm_id %}btn_asset_import{% endblock %}
diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html
index ea35972a0..35d6b6789 100644
--- a/apps/assets/templates/assets/admin_user_list.html
+++ b/apps/assets/templates/assets/admin_user_list.html
@@ -39,13 +39,6 @@ $(document).ready(function(){
var innerHtml = cellData.length > 8 ? cellData.substring(0, 24) + '...': cellData;
$(td).html('' + innerHtml + '');
}},
-{# {targets: 6, createdCell: function (td, cellData) {#}
-{# if (!cellData) {#}
-{# $(td).html('')#}
-{# } else {#}
-{# $(td).html('')#}
-{# }#}
-{# }},#}
{targets: 6, createdCell: function (td, cellData, rowData) {
var script_btn = '{% trans "Script" %}'.replace('99991937', cellData);
var update_btn = '{% trans "Update" %}'.replace('99991937', cellData);
@@ -55,7 +48,6 @@ $(document).ready(function(){
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);
});
diff --git a/apps/assets/templates/assets/asset_detail.html b/apps/assets/templates/assets/asset_detail.html
index 22af9acaa..bc3a7b147 100644
--- a/apps/assets/templates/assets/asset_detail.html
+++ b/apps/assets/templates/assets/asset_detail.html
@@ -96,7 +96,7 @@
{% trans 'Asset status' %}: |
- {{ asset.status }} |
+ {{ asset.get_status_display() }} |
{% trans 'Is active' %}: |
diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html
index c50361782..a2b1c306c 100644
--- a/apps/assets/templates/assets/asset_list.html
+++ b/apps/assets/templates/assets/asset_list.html
@@ -18,10 +18,11 @@
{% block table_search %}
@@ -32,7 +33,7 @@
{% for tag in tag_list %}
{% else %}
class="tagBtn2 label label-default">
@@ -47,7 +48,6 @@
{% block table_container %}
-
@@ -80,11 +80,13 @@
+{% include 'assets/_asset_import_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
+
{% endblock %}
\ No newline at end of file
diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html
index fb1e41323..77526ff37 100644
--- a/apps/assets/templates/assets/system_user_list.html
+++ b/apps/assets/templates/assets/system_user_list.html
@@ -1,43 +1,60 @@
{% extends '_base_list.html' %}
{% load i18n %}
{% load common_tags %}
-{% block content_left_head %}
+
+{% block table_search %}
+{% endblock %}
+
+{% block table_container %}
+
+
+{% endblock %}
+{% block custom_foot_js %}
+
{% endblock %}
-{% block table_head %}
- {% trans 'ID' %} |
- {% trans 'Name' %} |
- {% trans 'Username' %} |
- {% trans 'Asset num' %} |
- {% trans 'Asset group num' %} |
- {% trans 'Unreachable' %} |
- {% trans 'Comment' %} |
- |
-{% endblock %}
-{% block table_body %}
- {% for system_user in system_user_list %}
-
- {{ system_user.id }} |
-
-
- {{ system_user.name }}
-
- |
- {{ system_user.username }} |
- {{ system_user.get_assets|length }} |
- {{ system_user.asset_groups.count }} |
- {{ system_user.assets.count }} |
- {{ system_user.comment|truncatewords:4 }} |
-
-
- {% trans 'Script' %}
-
- {% trans 'Refresh' %}
- {% trans 'Update' %}
- {% trans 'Delete' %}
- |
-
- {% endfor %}
-{% endblock %}
+
diff --git a/apps/assets/urls/views_urls.py b/apps/assets/urls/views_urls.py
index 7b1f4e1bc..a2b4537a3 100644
--- a/apps/assets/urls/views_urls.py
+++ b/apps/assets/urls/views_urls.py
@@ -9,6 +9,8 @@ urlpatterns = [
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[0-9]+)/$', views.AssetDetailView.as_view(), name='asset-detail'),
url(r'^asset/(?P[0-9]+)/update/$', views.AssetUpdateView.as_view(), name='asset-update'),
url(r'^asset/(?P[0-9]+)/delete/$', views.AssetDeleteView.as_view(), name='asset-delete'),
diff --git a/apps/assets/views.py b/apps/assets/views.py
index 31619621d..7070d78bf 100644
--- a/apps/assets/views.py
+++ b/apps/assets/views.py
@@ -1,18 +1,32 @@
# 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.views.generic import TemplateView, ListView
+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 common.utils import int_seq
-from .utils import CreateAssetTagsMiXin,UpdateAssetTagsMiXin
-from .models import Asset, AssetGroup, IDC, AssetExtend, AdminUser, SystemUser, Tag
-from .forms import *
+from django.http import HttpResponse, JsonResponse
+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
@@ -33,7 +47,7 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
class AssetCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
model = Asset
tag_type = 'asset'
- form_class = AssetCreateForm
+ form_class = forms.AssetCreateForm
template_name = 'assets/asset_create.html'
success_url = reverse_lazy('assets:asset-list')
@@ -59,7 +73,7 @@ class AssetCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, CreateView):
class AssetModalCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, ListView):
model = Asset
- form_class = AssetCreateForm
+ form_class = forms.AssetCreateForm
template_name = 'assets/asset_modal_update.html'
success_url = reverse_lazy('assets:asset-list')
@@ -87,7 +101,7 @@ class AssetModalCreateView(AdminUserRequiredMixin, CreateAssetTagsMiXin, ListVie
class AssetUpdateView(AdminUserRequiredMixin, UpdateAssetTagsMiXin, UpdateView):
model = Asset
- form_class = AssetCreateForm
+ form_class = forms.AssetCreateForm
template_name = 'assets/asset_update.html'
success_url = reverse_lazy('assets:asset-list')
new_form = ''
@@ -214,7 +228,7 @@ class AssetModalListView(AdminUserRequiredMixin, ListView):
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
model = AssetGroup
- form_class = AssetGroupForm
+ form_class = forms.AssetGroupForm
template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list')
#ordering = '-id'
@@ -275,7 +289,7 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetGroup
- form_class = AssetGroupForm
+ form_class = forms.AssetGroupForm
template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list')
@@ -317,7 +331,7 @@ class IDCListView(AdminUserRequiredMixin, TemplateView):
class IDCCreateView(AdminUserRequiredMixin, CreateView):
model = IDC
- form_class = IDCForm
+ form_class = forms.IDCForm
template_name = 'assets/idc_create_update.html'
success_url = reverse_lazy('assets:idc-list')
@@ -339,7 +353,7 @@ class IDCCreateView(AdminUserRequiredMixin, CreateView):
class IDCUpdateView(AdminUserRequiredMixin, UpdateView):
model = IDC
- form_class = IDCForm
+ form_class = forms.IDCForm
template_name = 'assets/idc_create_update.html'
context_object_name = 'idc'
success_url = reverse_lazy('assets:idc-list')
@@ -408,7 +422,7 @@ class AdminUserListView(AdminUserRequiredMixin, TemplateView):
class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AdminUser
- form_class = AdminUserForm
+ form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html'
success_url = reverse_lazy('assets:admin-user-list')
@@ -434,7 +448,7 @@ class AdminUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser
- form_class = AdminUserForm
+ form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html'
def get_context_data(self, **kwargs):
@@ -478,39 +492,21 @@ class AdminUserDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('assets:admin-user-list')
-class SystemUserListView(AdminUserRequiredMixin, ListView):
- model = SystemUser
- paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
- context_object_name = 'system_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'),
- 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(SystemUserListView, self).get_context_data(**kwargs)
- def get_queryset(self):
- # Todo: Default order by lose asset connection num
- self.queryset = super(SystemUserListView, self).get_queryset()
- self.keyword = keyword = self.request.GET.get('keyword', '')
- self.sort = sort = self.request.GET.get('sort', '-date_created')
-
- if keyword:
- self.queryset = self.queryset.filter(Q(name__icontains=keyword) |
- Q(comment__icontains=keyword))
-
- if sort:
- self.queryset = self.queryset.order_by(sort)
- return self.queryset
-
class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = SystemUser
- form_class = SystemUserForm
+ form_class = forms.SystemUserForm
template_name = 'assets/system_user_create_update.html'
success_url = reverse_lazy('assets:system-user-list')
@@ -534,7 +530,7 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = SystemUser
- form_class = SystemUserForm
+ form_class = forms.SystemUserForm
template_name = 'assets/system_user_create_update.html'
def get_context_data(self, **kwargs):
@@ -636,7 +632,7 @@ class TagsListView(AdminUserRequiredMixin, ListView):
class AssetTagCreateView(AdminUserRequiredMixin, CreateView):
model = Tag
- form_class = AssetTagForm
+ form_class = forms.AssetTagForm
template_name = 'assets/asset_tag_create.html'
success_url = reverse_lazy('assets:asset-tag-list')
#ordering = '-id'
@@ -685,7 +681,7 @@ class AssetTagDetailView(SingleObjectMixin, AdminUserRequiredMixin, ListView):
class AssetTagUpdateView(AdminUserRequiredMixin, UpdateView):
model = Tag
- form_class = AssetTagForm
+ form_class = forms.AssetTagForm
template_name = 'assets/asset_tag_create.html'
success_url = reverse_lazy('assets:asset-tag-list')
@@ -711,3 +707,127 @@ class AssetTagDeleteView(AdminUserRequiredMixin, DeleteView):
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='application/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('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)
diff --git a/apps/audits/templates/audits/command_log_list.html b/apps/audits/templates/audits/command_log_list.html
index 19a9a63e6..03b786f3f 100644
--- a/apps/audits/templates/audits/command_log_list.html
+++ b/apps/audits/templates/audits/command_log_list.html
@@ -4,6 +4,7 @@
{% load common_tags %}
{% block content_left_head %}
+