[Update] 使用资产树,去掉集群和资产组

pull/1044/head
ibuler 2018-02-08 11:34:21 +08:00
parent 3bb6e08985
commit f82d939d15
28 changed files with 1306 additions and 1409 deletions

View File

@ -1,376 +0,0 @@
# coding:utf-8
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser, Label
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger
logger = get_logger(__file__)
class AssetCreateForm(forms.ModelForm):
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'public_ip', 'port', 'comment', 'cluster',
'nodes', 'is_active', 'admin_user', 'labels',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Select nodes')
}),
'cluster': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select cluster')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Select labels')
}),
'port': forms.TextInput(),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
'port': '* required',
'cluster': '* required',
'admin_user': _('Host level admin user, If not set using cluster admin user default')
}
def clean_admin_user(self):
cluster = self.cleaned_data.get('cluster')
admin_user = self.cleaned_data.get('admin_user')
if not admin_user and (cluster and not cluster.admin_user):
raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user']
def is_valid(self):
print(self.data)
result = super().is_valid()
if not result:
print(self.errors)
print(self.cleaned_data)
return result
class AssetUpdateForm(forms.ModelForm):
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'port', 'nodes', "cluster", 'is_active',
'public_ip', 'number', 'comment', 'admin_user', 'labels',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Select nodes')
}),
'cluster': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select cluster')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Select admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Select labels')
}),
'port': forms.TextInput(),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
'port': '* required',
'cluster': '* required',
'admin_user': _('Host level admin user, If not set using cluster admin user default')
}
def clean_admin_user(self):
cluster = self.cleaned_data.get('cluster')
admin_user = self.cleaned_data.get('admin_user')
if not admin_user and (cluster and not cluster.admin_user):
raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user']
class AssetBulkUpdateForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
required=True, help_text='* required',
label=_('Select assets'), queryset=Asset.objects.all(),
widget=forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _('Select assets')
}
)
)
port = forms.IntegerField(
label=_('Port'), required=False, min_value=1, max_value=65535,
)
cluster = forms.ModelChoiceField(
required=False, label=_("Cluster"), queryset=Cluster.objects.all(),
widget=forms.Select(
attrs={
'class': 'select2',
'data-placeholder': _('Select cluster')
}
)
)
class Meta:
model = Asset
fields = [
'assets', 'port', 'groups', "cluster", 'labels'
]
widgets = {
'groups': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}
),
'labels': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select labels')}
),
}
def save(self, commit=True):
changed_fields = []
for field in self._meta.fields:
if self.data.get(field) is not None:
changed_fields.append(field)
cleaned_data = {k: v for k, v in self.cleaned_data.items()
if k in changed_fields}
assets = cleaned_data.pop('assets')
groups = cleaned_data.pop('groups', [])
labels = cleaned_data.pop('labels', [])
assets = Asset.objects.filter(id__in=[asset.id for asset in assets])
assets.update(**cleaned_data)
if groups:
for asset in assets:
asset.groups.set(groups)
if labels:
for asset in assets:
asset.labels.set(labels)
return assets
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, **kwargs):
instance = kwargs.get('instance')
if instance:
initial = kwargs.get('initial', {})
initial.update({
'assets': instance.assets.all(),
})
kwargs['initial'] = initial
super().__init__(**kwargs)
def save(self, commit=True):
group = super().save(commit=commit)
assets = self.cleaned_data['assets']
group.assets.set(assets)
return group
class Meta:
model = AssetGroup
fields = [
"name", "comment",
]
help_texts = {
'name': '* required',
}
class ClusterForm(forms.ModelForm):
system_users = forms.ModelMultipleChoiceField(
queryset=SystemUser.objects.all(),
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select system users')}
),
label=_('System users'),
required=False,
help_text=_("Selected system users will be create at cluster assets"),
)
class Meta:
model = Cluster
fields = ['name', "bandwidth", "operator", 'contact', 'admin_user', 'system_users',
'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'})
}
help_texts = {
'name': '* required',
'admin_user': _("Cluster level admin user"),
}
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['system_users'] = kwargs['instance'].systemuser_set.all()
super().__init__(*args, **kwargs)
def save(self, commit=True):
instance = super().save(commit=commit)
system_users = self.cleaned_data['system_users']
instance.systemuser_set.set(system_users)
return instance
class PasswordAndKeyAuthForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(
widget=forms.PasswordInput, max_length=128,
strip=True, required=False,
help_text=_('Password or private key passphrase'),
label=_("Password"),
)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False, label=_("Private key"))
def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file']
password = self.cleaned_data['password']
if private_key_file:
key_string = private_key_file.read()
private_key_file.seek(0)
if not validate_ssh_private_key(key_string, password):
raise forms.ValidationError(_('Invalid private key'))
return private_key_file
def validate_password_key(self):
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '')
if not password and not private_key_file:
raise forms.ValidationError(_(
'Password and private key file must be input one'
))
def gen_keys(self):
password = self.cleaned_data.get('password', '') or None
private_key_file = self.cleaned_data['private_key_file']
public_key = private_key = None
if private_key_file:
private_key = private_key_file.read().strip().decode('utf-8')
public_key = ssh_pubkey_gen(private_key=private_key, password=password)
return private_key, public_key
class AdminUserForm(PasswordAndKeyAuthForm):
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
admin_user = super().save(commit=commit)
password = self.cleaned_data.get('password', '') or None
private_key, public_key = super().gen_keys()
admin_user.set_auth(password=password, public_key=public_key, private_key=private_key)
return admin_user
def clean(self):
super().clean()
if not self.instance:
super().validate_password_key()
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(PasswordAndKeyAuthForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False)
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super().save()
password = self.cleaned_data.get('password', '') or None
auto_generate_key = self.cleaned_data.get('auto_generate_key', False)
private_key, public_key = super().gen_keys()
if auto_generate_key:
logger.info('Auto generate key and set system user auth')
system_user.auto_gen_auth()
else:
system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
return system_user
def clean(self):
super().clean()
auto_generate = self.cleaned_data.get('auto_generate_key')
if not self.instance and not auto_generate:
super().validate_password_key()
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key',
'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'cluster', 'priority',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'cluster': forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _(' Select clusters')
}
),
}
help_texts = {
'name': '* required',
'username': '* required',
'cluster': _('If auto push checked, system user will be create at cluster assets'),
'auto_push': _('Auto push system user to asset'),
'priority': _('High level will be using login asset as default, if user was granted more than 2 system user'),
}
class FileForm(forms.Form):
file = forms.FileField()
class LabelForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(), label=_('Asset'), required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
class Meta:
model = Label
fields = ['name', 'value', 'assets']
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super().__init__(*args, **kwargs)
def save(self, commit=True):
label = super().save(commit=commit)
assets = self.cleaned_data['assets']
label.assets.set(assets)
return label

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
#
from .asset import *
from .label import *
from .user import *

114
apps/assets/forms/asset.py Normal file
View File

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from ..models import Asset
from common.utils import get_logger
logger = get_logger(__file__)
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm']
class AssetCreateForm(forms.ModelForm):
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'public_ip', 'port', 'comment',
'nodes', 'is_active', 'admin_user', 'labels',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Nodes')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Labels')
}),
'port': forms.TextInput(),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
'port': '* required',
'admin_user': _('')
}
class AssetUpdateForm(forms.ModelForm):
class Meta:
model = Asset
fields = [
'hostname', 'ip', 'port', 'nodes', 'is_active',
'public_ip', 'number', 'comment', 'admin_user', 'labels',
]
widgets = {
'nodes': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Nodes')
}),
'admin_user': forms.Select(attrs={
'class': 'select2', 'data-placeholder': _('Admin user')
}),
'labels': forms.SelectMultiple(attrs={
'class': 'select2', 'data-placeholder': _('Labels')
}),
'port': forms.TextInput(),
}
help_texts = {
'hostname': '* required',
'ip': '* required',
'port': '* required',
'cluster': '* required',
'admin_user': _('')
}
class AssetBulkUpdateForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
required=True, help_text='* required',
label=_('Select assets'), queryset=Asset.objects.all(),
widget=forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _('Select assets')
}
)
)
port = forms.IntegerField(
label=_('Port'), required=False, min_value=1, max_value=65535,
)
class Meta:
model = Asset
fields = [
'assets', 'port', 'labels', 'admin_user'
]
widgets = {
'admin_user': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Admin user')}
),
'labels': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Labels')}
),
}
def save(self, commit=True):
changed_fields = []
for field in self._meta.fields:
if self.data.get(field) is not None:
changed_fields.append(field)
cleaned_data = {k: v for k, v in self.cleaned_data.items()
if k in changed_fields}
assets = cleaned_data.pop('assets')
labels = cleaned_data.pop('labels', [])
assets = Asset.objects.filter(id__in=[asset.id for asset in assets])
assets.update(**cleaned_data)
if labels:
for asset in assets:
asset.labels.set(labels)
return assets

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from ..models import Label, Asset
__all__ = ['LabelForm']
class LabelForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(), label=_('Asset'), required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
class Meta:
model = Label
fields = ['name', 'value', 'assets']
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super().__init__(*args, **kwargs)
def save(self, commit=True):
label = super().save(commit=commit)
assets = self.cleaned_data['assets']
label.assets.set(assets)
return label

135
apps/assets/forms/user.py Normal file
View File

@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
#
from django import forms
from django.utils.translation import gettext_lazy as _
from ..models import AdminUser, SystemUser
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
logger = get_logger(__file__)
__all__ = [
'FileForm', 'SystemUserForm', 'AdminUserForm',
]
class FileForm(forms.Form):
file = forms.FileField()
class PasswordAndKeyAuthForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(
widget=forms.PasswordInput, max_length=128,
strip=True, required=False,
help_text=_('Password or private key passphrase'),
label=_("Password"),
)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False, label=_("Private key"))
def clean_private_key_file(self):
private_key_file = self.cleaned_data['private_key_file']
password = self.cleaned_data['password']
if private_key_file:
key_string = private_key_file.read()
private_key_file.seek(0)
if not validate_ssh_private_key(key_string, password):
raise forms.ValidationError(_('Invalid private key'))
return private_key_file
def validate_password_key(self):
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '')
if not password and not private_key_file:
raise forms.ValidationError(_(
'Password and private key file must be input one'
))
def gen_keys(self):
password = self.cleaned_data.get('password', '') or None
private_key_file = self.cleaned_data['private_key_file']
public_key = private_key = None
if private_key_file:
private_key = private_key_file.read().strip().decode('utf-8')
public_key = ssh_pubkey_gen(private_key=private_key, password=password)
return private_key, public_key
class AdminUserForm(PasswordAndKeyAuthForm):
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
admin_user = super().save(commit=commit)
password = self.cleaned_data.get('password', '') or None
private_key, public_key = super().gen_keys()
admin_user.set_auth(password=password, public_key=public_key, private_key=private_key)
return admin_user
def clean(self):
super().clean()
if not self.instance:
super().validate_password_key()
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(PasswordAndKeyAuthForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False)
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super().save()
password = self.cleaned_data.get('password', '') or None
auto_generate_key = self.cleaned_data.get('auto_generate_key', False)
private_key, public_key = super().gen_keys()
if auto_generate_key:
logger.info('Auto generate key and set system user auth')
system_user.auto_gen_auth()
else:
system_user.set_auth(password=password, private_key=private_key, public_key=public_key)
return system_user
def clean(self):
super().clean()
auto_generate = self.cleaned_data.get('auto_generate_key')
if not self.instance and not auto_generate:
super().validate_password_key()
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key',
'password', 'private_key_file', 'auto_push', 'sudo',
'comment', 'shell', 'cluster', 'priority',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'cluster': forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _(' Select clusters')
}
),
}
help_texts = {
'name': '* required',
'username': '* required',
'cluster': _('If auto push checked, system user will be create at cluster assets'),
'auto_push': _('Auto push system user to asset'),
'priority': _('High level will be using login asset as default, if user was granted more than 2 system user'),
}

View File

@ -28,19 +28,22 @@ def default_cluster():
return cluster.id return cluster.id
def default_node():
from .tree import Node
return Node.root()
class Asset(models.Model): class Asset(models.Model):
# Important # Important
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True) ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port')) port = models.IntegerField(default=22, verbose_name=_('Port'))
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
cluster = models.ForeignKey(Cluster, related_name='assets', default=default_cluster, on_delete=models.SET_DEFAULT, verbose_name=_('Cluster'))
nodes = models.ManyToManyField('assets.Node', blank=True, related_name='assets', verbose_name=_("Nodes"))
is_active = models.BooleanField(default=True, verbose_name=_('Is active')) is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
# Auth # Auth
admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user")) admin_user = models.ForeignKey('assets.AdminUser', on_delete=models.PROTECT, verbose_name=_("Admin user"))
# Some information # Some information
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))

View File

@ -12,7 +12,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
""" """
资产的数据结构 资产的数据结构
""" """
cluster_name = serializers.SerializerMethodField()
class Meta: class Meta:
model = Asset model = Asset
@ -27,10 +26,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
]) ])
return fields return fields
@staticmethod
def get_cluster_name(obj):
return obj.cluster.name
class AssetGrantedSerializer(serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
""" """

View File

@ -34,8 +34,6 @@ class SystemUserSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_assets_amount(obj): def get_assets_amount(obj):
amount = 0 amount = 0
for cluster in obj.cluster.all():
amount += cluster.assets.all().count()
return amount return amount

View File

@ -39,91 +39,78 @@ def on_asset_created(sender, instance=None, created=False, **kwargs):
logger.info("Asset `{}` create signal received".format(instance)) logger.info("Asset `{}` create signal received".format(instance))
update_asset_hardware_info_on_created(instance) update_asset_hardware_info_on_created(instance)
test_asset_conn_on_created(instance) test_asset_conn_on_created(instance)
push_cluster_system_users_to_asset(instance) # push_cluster_system_users_to_asset(instance)
@receiver(post_init, sender=Asset) # def push_to_cluster_assets_on_system_user_created_or_update(system_user):
def on_asset_init(sender, instance, created=False, **kwargs): # if not system_user.auto_push:
if instance and created is False: # return
instance.__original_cluster = instance.cluster # logger.debug("Push system user `{}` to cluster assets".format(system_user.name))
# for cluster in system_user.cluster.all():
# task_name = _("Push system user to cluster assets: {}->{}").format(
# cluster.name, system_user.name
# )
# assets = cluster.assets.all()
# push_system_user_util.delay([system_user], assets, task_name)
@receiver(post_save, sender=Asset) # @receiver(post_save, sender=SystemUser)
def on_asset_cluster_changed(sender, instance=None, created=False, **kwargs): # def on_system_user_created_or_updated(sender, instance=None, **kwargs):
if instance and created is False and instance.cluster != instance.__original_cluster: # if instance and instance.auto_push:
logger.info("Asset cluster changed signal received") # logger.info("System user `{}` create or update signal received".format(instance))
push_cluster_system_users_to_asset(instance) # push_to_cluster_assets_on_system_user_created_or_update(instance)
#
#
def push_to_cluster_assets_on_system_user_created_or_update(system_user): # @receiver(post_init, sender=Cluster, dispatch_uid="my_unique_identifier")
if not system_user.auto_push: # def on_cluster_init(sender, instance, **kwargs):
return # instance.__original_assets = tuple(instance.assets.values_list('pk', flat=True))
logger.debug("Push system user `{}` to cluster assets".format(system_user.name)) # instance.__origin_system_users = tuple(instance.systemuser_set.values_list('pk', flat=True))
for cluster in system_user.cluster.all(): #
task_name = _("Push system user to cluster assets: {}->{}").format( #
cluster.name, system_user.name # @receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier")
) # def on_cluster_assets_changed(sender, instance, **kwargs):
assets = cluster.assets.all() # assets_origin = instance.__original_assets
push_system_user_util.delay([system_user], assets, task_name) # assets_new = instance.assets.values_list('pk', flat=True)
# assets_added = set(assets_new) - set(assets_origin)
# if assets_added:
@receiver(post_save, sender=SystemUser) # logger.debug("Receive cluster change assets signal")
def on_system_user_created_or_updated(sender, instance=None, **kwargs): # logger.debug("Push cluster `{}` system users to: {}".format(
if instance and instance.auto_push: # instance, ', '.join([str(asset) for asset in assets_added])
logger.info("System user `{}` create or update signal received".format(instance)) # ))
push_to_cluster_assets_on_system_user_created_or_update(instance) # assets = []
# for asset_id in assets_added:
# try:
@receiver(post_init, sender=Cluster, dispatch_uid="my_unique_identifier") # asset = Asset.objects.get(pk=asset_id)
def on_cluster_init(sender, instance, **kwargs): # except Asset.DoesNotExist:
instance.__original_assets = tuple(instance.assets.values_list('pk', flat=True)) # continue
instance.__origin_system_users = tuple(instance.systemuser_set.values_list('pk', flat=True)) # else:
# assets.append(asset)
# system_users = [s for s in instance.systemuser_set.all() if s.auto_push]
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier") # task_name = _("Push system user to assets")
def on_cluster_assets_changed(sender, instance, **kwargs): # push_system_user_util.delay(system_users, assets, task_name)
assets_origin = instance.__original_assets #
assets_new = instance.assets.values_list('pk', flat=True) #
assets_added = set(assets_new) - set(assets_origin) # @receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier2")
if assets_added: # def on_cluster_system_user_changed(sender, instance, **kwargs):
logger.debug("Receive cluster change assets signal") # system_users_origin = instance.__origin_system_users
logger.debug("Push cluster `{}` system users to: {}".format( # system_user_new = instance.systemuser_set.values_list('pk', flat=True)
instance, ', '.join([str(asset) for asset in assets_added]) # system_users_added = set(system_user_new) - set(system_users_origin)
)) # if system_users_added:
assets = [] # logger.debug("Receive cluster change system users signal")
for asset_id in assets_added: # system_users = []
try: # for system_user_id in system_users_added:
asset = Asset.objects.get(pk=asset_id) # try:
except Asset.DoesNotExist: # system_user = SystemUser.objects.get(pk=system_user_id)
continue # except SystemUser.DoesNotExist:
else: # continue
assets.append(asset) # else:
system_users = [s for s in instance.systemuser_set.all() if s.auto_push] # system_users.append(system_user)
task_name = _("Push system user to assets") # logger.debug("Push new system users `{}` to cluster `{}` assets".format(
push_system_user_util.delay(system_users, assets, task_name) # ','.join([s.name for s in system_users]), instance
# ))
# task_name = _(
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier2") # "Push system user to cluster assets: {}->{}").format(
def on_cluster_system_user_changed(sender, instance, **kwargs): # instance.name, ', '.join(s.name for s in system_users)
system_users_origin = instance.__origin_system_users # )
system_user_new = instance.systemuser_set.values_list('pk', flat=True)
system_users_added = set(system_user_new) - set(system_users_origin)
if system_users_added:
logger.debug("Receive cluster change system users signal")
system_users = []
for system_user_id in system_users_added:
try:
system_user = SystemUser.objects.get(pk=system_user_id)
except SystemUser.DoesNotExist:
continue
else:
system_users.append(system_user)
logger.debug("Push new system users `{}` to cluster `{}` assets".format(
','.join([s.name for s in system_users]), instance
))
task_name = _(
"Push system user to cluster assets: {}->{}").format(
instance.name, ', '.join(s.name for s in system_users)
)
push_system_user_util.delay(system_users, instance.assets.all(), task_name) push_system_user_util.delay(system_users, instance.assets.all(), task_name)

View File

@ -0,0 +1,280 @@
{% 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>
{% 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 table_container %}
<div class="uc pull-left m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset" %} </a></div>
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
{% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="asset_list_table" >
<thead>
<tr>
<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 'Cluster' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Reachable' %}</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 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_import_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script type="text/javascript">
function initTable() {
var options = {
ele: $('#asset_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.cluster_name)
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.hardware_info)
}},
{targets: 6, 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: 7, createdCell: function (td, cellData) {
if (cellData == 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{targets: 8, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "cluster"},
{data: "cpu_cores"}, {data: "is_active", orderable: false },
{data: "is_connective", orderable: false}, {data: "id", orderable: false }
],
op_html: $('#actions').html()
};
return jumpserver.initServerSideDataTable(options);
}
$(document).ready(function(){
initTable();
$(".select2").select2();
})
.on('click', '.labels li', function () {
var val = $(this).text();
$("#asset_list_table_filter input").val(val);
jumpserver.table.search(val).draw();
})
.on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data();
var assets = [];
$.each(rows, function (index, obj) {
assets.push(obj.id)
});
$.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');
}
})
})
.on('click', '#btn_asset_import', 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=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", 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_list_table').DataTable();
var id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push(this.data().id);
});
if (id_list.length === 0) {
return false;
}
var the_url = "{% url 'api-assets:asset-list' %}";
function doDeactive() {
var data = [];
$.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": false};
data.push(obj);
});
function success() {
location.reload()
}
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success
});
}
function doActive() {
var data = [];
$.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": true};
data.push(obj);
});
function success() {
location.reload();
}
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success
});
}
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(id_list);
APIUpdateAttr({
url: url_delete,
method: 'DELETE',
success: success,
error: fail
});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
var id_list_string = id_list.join(',');
var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string;
location.href = url
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
case 'active':
doActive();
break;
default:
break;
}
});
</script>
{% endblock %}

View File

@ -17,7 +17,6 @@
{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %} {% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>

View File

@ -1,75 +1,127 @@
{% extends '_base_list.html' %} {% extends 'base.html' %}
{% load i18n %}
{% load static %} {% load static %}
{% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
<style type="text/css">
div#rMenu {
position:absolute;
visibility:hidden;
text-align: left;
top: 100%;
left: 0;
z-index: 1000;
float: left;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
background-clip: padding-box;
}
div#rMenu li{
margin: 1px 0;
cursor: pointer;
{#list-style: none outside none;#}
}
.dropdown a:hover {
background-color: #f1f1f1
}
</style>
{% endblock %} {% endblock %}
{% block content_left_head %}{% endblock %}
{% block table_search %} {% block content %}
<div class="html5buttons"> <div class="wrapper wrapper-content">
<div class="dt-buttons btn-group"> <div class="row">
<a class="btn btn-default btn_import" data-toggle="modal" data-target="#asset_import_modal" tabindex="0"> <div class="col-lg-3">
<span>{% trans "Import" %}</span> <div class="ibox float-e-margins">
</a> <div class="ibox-content mailbox-content" style="padding-top: 0">
<a class="btn btn-default btn_export" tabindex="0"> <div class="file-manager ">
<span>{% trans "Export" %}</span> <div id="assetTree" class="ztree">
</a> </div>
</div>
</div>
{% endblock %}
{% block table_container %} <div class="clearfix"></div>
<div class="uc pull-left m-r-5"><a href="{% url "assets:asset-create" %}" class="btn btn-sm btn-primary"> {% trans "Create asset" %} </a></div> </div>
<div class="btn-group" style="float: right"> </div>
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button> </div>
<ul class="dropdown-menu labels"> </div>
{% for label in labels %} <div class="col-lg-9 animated fadeInRight">
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li> <div class="mail-box-header">
{% endfor %} <div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
<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>
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
{% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="asset_list_table" >
<thead>
<tr>
<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 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Reachable' %}</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="remove">{% trans 'Remove from this node' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
<option value="active">{% trans 'Active 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>
</div>
</div>
</div>
</div>
<div id="rMenu">
<ul class="dropdown-menu">
<li id="menu_asset_create" class="btn-create-asset" tabindex="-1"><a>{% trans 'Create asset' %}</a></li>
<li id="menu_asset_add" class="btn-add-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a>{% trans 'Add asset' %}</a></li>
<li class="divider"></li>
<li id="m_create" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Add node' %}</a></li>
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>{% trans 'Rename node' %}</a></li>
<li class="divider"></li>
<li id="m_del" tabindex="-1" onclick="removeTreeNode();"><a>{% trans 'Delete node' %}</a></li>
</ul> </ul>
</div> </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 'Cluster' %}</th>
<th class="text-center">{% trans 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Reachable' %}</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 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_import_modal.html' %} {% include 'assets/_asset_import_modal.html' %}
{% include 'assets/_asset_list_modal.html' %}
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script> <script>
<script type="text/javascript"> var zTree, rMenu, asset_table;
function initTable() { function initTable() {
var options = { var options = {
ele: $('#asset_list_table'), ele: $('#asset_list_table'),
@ -79,21 +131,18 @@ function initTable() {
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>'; var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
{targets: 4, createdCell: function (td, cellData, rowData) { {targets: 3, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.cluster_name)
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.hardware_info) $(td).html(rowData.hardware_info)
}}, }},
{targets: 6, createdCell: function (td, cellData) { {targets: 4, createdCell: function (td, cellData) {
if (!cellData) { if (!cellData) {
$(td).html('<i class="fa fa-times text-danger"></i>') $(td).html('<i class="fa fa-times text-danger"></i>')
} else { } else {
$(td).html('<i class="fa fa-check text-navy"></i>') $(td).html('<i class="fa fa-check text-navy"></i>')
} }
}}, }},
{targets: 7, createdCell: function (td, cellData) { {targets: 5, createdCell: function (td, cellData) {
if (cellData == 'Unknown'){ if (cellData === 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>') $(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) { } else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>') $(td).html('<i class="fa fa-circle text-danger"></i>')
@ -101,7 +150,7 @@ function initTable() {
$(td).html('<i class="fa fa-circle text-navy"></i>') $(td).html('<i class="fa fa-circle text-navy"></i>')
} }
}}, }},
{targets: 8, createdCell: function (td, cellData, rowData) { {targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData); var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
@ -109,25 +158,195 @@ function initTable() {
], ],
ajax_url: '{% url "api-assets:asset-list" %}', ajax_url: '{% url "api-assets:asset-list" %}',
columns: [ columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "cluster"},
{data: "cpu_cores"}, {data: "is_active", orderable: false }, {data: "cpu_cores"}, {data: "is_active", orderable: false },
{data: "is_connective", orderable: false}, {data: "id", orderable: false } {data: "is_connective", orderable: false}, {data: "id", orderable: false }
], ],
op_html: $('#actions').html() op_html: $('#actions').html()
}; };
return jumpserver.initServerSideDataTable(options); asset_table = jumpserver.initServerSideDataTable(options);
return asset_table
}
function addTreeNode() {
hideRMenu();
var parentNode = zTree.getSelectedNodes()[0];
if (!parentNode){
return
}
var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.id );
$.post(url, {}, function (data, status){
if (status === "success") {
var newNode = {
name: data["value"],
id: data["id"],
pId: parentNode.id
};
newNode.checked = zTree.getSelectedNodes()[0].checked;
zTree.addNodes(parentNode, 0, newNode);
} else {
alert("{% trans 'Create node failed' %}")
}
});
}
function removeTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
return
}
if (current_node.children && current_node.children.length > 0) {
alert("{% trans 'Have child node, cancel' %}")
} else {
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id );
$.ajax({
url: url,
method: "DELETE",
success: function () {
zTree.removeNode(current_node);
}
});
}
}
function editTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
return
}
zTree.editName(current_node);
}
function OnRightClick(event, treeId, treeNode) {
if (!treeNode && event.target.tagName.toLowerCase() !== "button" && $(event.target).parents("a").length === 0) {
zTree.cancelSelectedNode();
showRMenu("root", event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
zTree.selectNode(treeNode);
showRMenu("node", event.clientX, event.clientY);
}
}
function showRMenu(type, x, y) {
$("#rMenu ul").show();
{#if (type === "root") {#}
{# return#}
{# } else {#}
{# $("#m_del").show();#}
{# $("#m_check").show();#}
{# $("#m_unCheck").show();#}
{# }#}
x -= 220;
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
$("body").bind("mousedown", onBodyMouseDown);
}
function beforeClick(treeId, treeNode, clickFlag) {
return true;
}
function hideRMenu() {
if (rMenu) rMenu.css({"visibility": "hidden"});
$("body").unbind("mousedown", onBodyMouseDown);
}
function onBodyMouseDown(event){
if (!(event.target.id === "rMenu" || $(event.target).parents("#rMenu").length>0)) {
rMenu.css({"visibility" : "hidden"});
}
}
function onRename(event, treeId, treeNode, isCancel){
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.id);
var data = {"value": treeNode.name};
if (isCancel){
return
}
APIUpdateAttr({
url: url,
body: JSON.stringify(data),
method: "PATCH"
})
}
function onSelected(event, treeNode) {
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
setCookie('node_selected', treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
function selectQueryNode() {
var query_node_id = $.getUrlParam("node");
var cookie_node_id = getCookie('node_selected');
var node;
var node_id;
if (query_node_id !== null) {
node_id = query_node_id
} else if (cookie_node_id !== null) {
node_id = cookie_node_id;
}
node = zTree.getNodesByParam("id", node_id, null);
if (node){
zTree.selectNode(node[0]);
}
}
function initTree() {
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onRightClick: OnRightClick,
beforeClick: beforeClick,
onRename: onRename,
onSelected: onSelected
}
};
var zNodes = [];
$.get("{% url 'api-assets:node-list' %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
});
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
} }
$(document).ready(function(){ $(document).ready(function(){
initTable(); initTable();
$(".select2").select2(); initTree();
}) })
.on('click', '.labels li', function () { .on('click', '.labels li', function () {
var val = $(this).text(); var val = $(this).text();
$("#asset_list_table_filter input").val(val); $("#asset_list_table_filter input").val(val);
jumpserver.table.search(val).draw(); asset_table.search(val).draw();
}) })
.on('click', '.btn_export', function () { .on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable(); var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data(); var rows = $data_table.rows('.selected').data();
@ -167,7 +386,16 @@ $(document).ready(function(){
} }
$form.ajaxSubmit({success: success}); $form.ajaxSubmit({success: success});
}) })
.on('click', '.btn-create-asset', function () {
var url = "{% url 'assets:asset-create' %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
url += "?node_id=" + current_node.id;
}
window.open(url);
})
.on('click', '.btn_asset_delete', function () { .on('click', '.btn_asset_delete', function () {
var $this = $(this); var $this = $(this);
var $data_table = $("#asset_list_table").DataTable(); var $data_table = $("#asset_list_table").DataTable();
@ -198,7 +426,7 @@ $(document).ready(function(){
data.push(obj); data.push(obj);
}); });
function success() { function success() {
location.reload() asset_table.ajax.reload()
} }
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
@ -214,7 +442,7 @@ $(document).ready(function(){
data.push(obj); data.push(obj);
}); });
function success() { function success() {
location.reload(); asset_table.ajax.reload()
} }
APIUpdateAttr({ APIUpdateAttr({
url: the_url, url: the_url,
@ -258,6 +486,31 @@ $(document).ready(function(){
var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string; var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string;
location.href = url location.href = url
} }
function doRemove() {
var current_node;
var nodes = zTree.getSelectedNodes();
if (nodes && nodes.length === 1) {
current_node = nodes[0]
} else {
return
}
var data = {
'assets': id_list
};
var success = function () {
asset_table.ajax.reload()
};
APIUpdateAttr({
'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/remove/',
'method': 'PUT',
'body': JSON.stringify(data),
'success': success
})
}
switch(action) { switch(action) {
case 'deactive': case 'deactive':
doDeactive(); doDeactive();
@ -271,10 +524,13 @@ $(document).ready(function(){
case 'active': case 'active':
doActive(); doActive();
break; break;
case 'remove':
doRemove();
break;
default: default:
break; break;
} }
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -22,7 +22,6 @@
{% bootstrap_field form.hostname layout="horizontal" %} {% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %} {% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %} {% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.cluster layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %} {% bootstrap_field form.public_ip layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>

View File

@ -1,537 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/jstree/style.min.css' %}" rel="stylesheet">
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
<style type="text/css">
div#rMenu {
position:absolute;
visibility:hidden;
text-align: left;
top: 100%;
left: 0;
z-index: 1000;
float: left;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
background-clip: padding-box;
}
div#rMenu li{
margin: 1px 0;
cursor: pointer;
{#list-style: none outside none;#}
}
.dropdown a:hover {
background-color: #f1f1f1
}
</style>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content">
<div class="row">
<div class="col-lg-3">
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0">
<div class="file-manager ">
<div id="assetTree" class="ztree">
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-9 animated fadeInRight">
<div class="mail-box-header">
<div class="uc pull-left m-r-5"><a class="btn btn-sm btn-primary btn-create-asset"> {% trans "Create asset" %} </a></div>
<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>
<div class="btn-group" style="float: right">
<button data-toggle="dropdown" class="btn btn-default btn-sm dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>
<ul class="dropdown-menu labels">
{% for label in labels %}
<li><a style="font-weight: bolder">{{ label.name }}:{{ label.value }}</a></li>
{% endfor %}
</ul>
</div>
<table class="table table-striped table-bordered table-hover " id="asset_list_table" >
<thead>
<tr>
<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 'Hardware' %}</th>
<th class="text-center">{% trans 'Active' %}</th>
<th class="text-center">{% trans 'Reachable' %}</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="remove">{% trans 'Remove from this node' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
<option value="active">{% trans 'Active 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>
</div>
</div>
</div>
</div>
<div id="rMenu">
<ul class="dropdown-menu">
<li id="menu_asset_create" class="btn-create-asset" tabindex="-1"><a>{% trans 'Create asset' %}</a></li>
<li id="menu_asset_add" class="btn-add-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a>{% trans 'Add asset' %}</a></li>
<li class="divider"></li>
<li id="m_create" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Add node' %}</a></li>
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>{% trans 'Rename node' %}</a></li>
<li class="divider"></li>
<li id="m_del" tabindex="-1" onclick="removeTreeNode();"><a>{% trans 'Delete node' %}</a></li>
</ul>
</div>
{% include 'assets/_asset_import_modal.html' %}
{% include 'assets/_asset_list_modal.html' %}
{% endblock %}
{% block custom_foot_js %}
<script>
var zTree, rMenu, asset_table;
function initTable() {
var options = {
ele: $('#asset_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
{targets: 3, createdCell: function (td, cellData, rowData) {
$(td).html(rowData.hardware_info)
}},
{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) {
if (cellData === 'Unknown'){
$(td).html('<i class="fa fa-circle text-warning"></i>')
} else if (!cellData) {
$(td).html('<i class="fa fa-circle text-danger"></i>')
} else {
$(td).html('<i class="fa fa-circle text-navy"></i>')
}
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
$(td).html(update_btn + del_btn)
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "cpu_cores"}, {data: "is_active", orderable: false },
{data: "is_connective", orderable: false}, {data: "id", orderable: false }
],
op_html: $('#actions').html()
};
asset_table = jumpserver.initServerSideDataTable(options);
return asset_table
}
function addTreeNode() {
hideRMenu();
var parentNode = zTree.getSelectedNodes()[0];
if (!parentNode){
return
}
var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.id );
$.post(url, {}, function (data, status){
if (status === "success") {
var newNode = {
name: data["value"],
id: data["id"],
pId: parentNode.id
};
newNode.checked = zTree.getSelectedNodes()[0].checked;
zTree.addNodes(parentNode, 0, newNode);
} else {
alert("{% trans 'Create node failed' %}")
}
});
}
function removeTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
return
}
if (current_node.children && current_node.children.length > 0) {
alert("{% trans 'Have child node, cancel' %}")
} else {
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id );
$.ajax({
url: url,
method: "DELETE",
success: function () {
zTree.removeNode(current_node);
}
});
}
}
function editTreeNode() {
hideRMenu();
var current_node = zTree.getSelectedNodes()[0];
if (!current_node){
return
}
zTree.editName(current_node);
}
function OnRightClick(event, treeId, treeNode) {
if (!treeNode && event.target.tagName.toLowerCase() !== "button" && $(event.target).parents("a").length === 0) {
zTree.cancelSelectedNode();
showRMenu("root", event.clientX, event.clientY);
} else if (treeNode && !treeNode.noR) {
zTree.selectNode(treeNode);
showRMenu("node", event.clientX, event.clientY);
}
}
function showRMenu(type, x, y) {
$("#rMenu ul").show();
{#if (type === "root") {#}
{# return#}
{# } else {#}
{# $("#m_del").show();#}
{# $("#m_check").show();#}
{# $("#m_unCheck").show();#}
{# }#}
x -= 220;
rMenu.css({"top":y+"px", "left":x+"px", "visibility":"visible"});
$("body").bind("mousedown", onBodyMouseDown);
}
function beforeClick(treeId, treeNode, clickFlag) {
return true;
}
function hideRMenu() {
if (rMenu) rMenu.css({"visibility": "hidden"});
$("body").unbind("mousedown", onBodyMouseDown);
}
function onBodyMouseDown(event){
if (!(event.target.id === "rMenu" || $(event.target).parents("#rMenu").length>0)) {
rMenu.css({"visibility" : "hidden"});
}
}
function onRename(event, treeId, treeNode, isCancel){
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.id);
var data = {"value": treeNode.name};
if (isCancel){
return
}
APIUpdateAttr({
url: url,
body: JSON.stringify(data),
method: "PATCH"
})
}
function onSelected(event, treeNode) {
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
setCookie('node_selected', treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
function selectQueryNode() {
var query_node_id = $.getUrlParam("node");
var cookie_node_id = getCookie('node_selected');
var node;
var node_id;
if (query_node_id !== null) {
node_id = query_node_id
} else if (cookie_node_id !== null) {
node_id = cookie_node_id;
}
node = zTree.getNodesByParam("id", node_id, null);
if (node){
zTree.selectNode(node[0]);
}
}
function initTree() {
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onRightClick: OnRightClick,
beforeClick: beforeClick,
onRename: onRename,
onSelected: onSelected
}
};
var zNodes = [];
$.get("{% url 'api-assets:node-list' %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
});
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
}
$(document).ready(function(){
initTable();
initTree();
})
.on('click', '.labels li', function () {
var val = $(this).text();
$("#asset_list_table_filter input").val(val);
jumpserver.table.search(val).draw();
})
.on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data();
var assets = [];
$.each(rows, function (index, obj) {
assets.push(obj.id)
});
$.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');
}
})
})
.on('click', '#btn_asset_import', 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-create-asset', function () {
var url = "{% url 'assets:asset-create' %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
url += "?node_id=" + current_node.id;
}
window.open(url);
})
.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=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", 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_list_table').DataTable();
var id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push(this.data().id);
});
if (id_list.length === 0) {
return false;
}
var the_url = "{% url 'api-assets:asset-list' %}";
function doDeactive() {
var data = [];
$.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": false};
data.push(obj);
});
function success() {
asset_table.ajax.reload()
}
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success
});
}
function doActive() {
var data = [];
$.each(id_list, function(index, object_id) {
var obj = {"pk": object_id, "is_active": true};
data.push(obj);
});
function success() {
asset_table.ajax.reload()
}
APIUpdateAttr({
url: the_url,
method: 'PATCH',
body: JSON.stringify(data),
success: success
});
}
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(id_list);
APIUpdateAttr({
url: url_delete,
method: 'DELETE',
success: success,
error: fail
});
$data_table.ajax.reload();
jumpserver.checked = false;
});
}
function doUpdate() {
var id_list_string = id_list.join(',');
var url = "{% url 'assets:asset-bulk-update' %}?assets_id=" + id_list_string;
location.href = url
}
function doRemove() {
var current_node;
var nodes = zTree.getSelectedNodes();
if (nodes && nodes.length === 1) {
current_node = nodes[0]
} else {
return
}
var data = {
'assets': id_list
};
var success = function () {
asset_table.ajax.reload()
};
APIUpdateAttr({
'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/remove/',
'method': 'PUT',
'body': JSON.stringify(data),
'success': success
})
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
case 'active':
doActive();
break;
case 'remove':
doRemove();
break;
default:
break;
}
});
</script>
{% endblock %}

View File

@ -5,4 +5,4 @@ from .asset import *
from .system_user import * from .system_user import *
from .admin_user import * from .admin_user import *
from .label import * from .label import *
from .tree import * from .node import *

View File

@ -41,7 +41,7 @@ logger = get_logger(__file__)
class AssetListView(AdminUserRequiredMixin, TemplateView): class AssetListView(AdminUserRequiredMixin, TemplateView):
template_name = 'assets/tree.html' template_name = 'assets/asset_list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {

View File

@ -10,7 +10,7 @@ from users.permissions import IsValidUser, IsSuperUser, IsSuperUserOrAppUser
from .utils import NodePermissionUtil from .utils import NodePermissionUtil
from .models import NodePermission from .models import NodePermission
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, \ from .hands import AssetGrantedSerializer, User, UserGroup, Asset, \
NodeGrantedSerializer, SystemUser NodeGrantedSerializer, SystemUser, NodeSerializer
from . import serializers from . import serializers
@ -30,8 +30,10 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
node_id = self.request.query_params.get('node_id') node_id = self.request.query_params.get('node_id')
if node_id: if node_id:
queryset = queryset.filter(node__id=node_id) queryset = queryset.filter(node__id=node_id)
return queryset return queryset
@ -62,6 +64,20 @@ class UserGrantedAssetsApi(ListAPIView):
return super().get_permissions() return super().get_permissions()
class UserGrantedNodesApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = NodeSerializer
def get_queryset(self):
user_id = self.kwargs.get('pk', '')
if user_id:
user = get_object_or_404(User, id=user_id)
else:
user = self.request.user
nodes = NodePermissionUtil.get_user_nodes(user)
return nodes.keys()
class UserGrantedNodesWithAssetsApi(ListAPIView): class UserGrantedNodesWithAssetsApi(ListAPIView):
""" """
授权用户的资产组这里的资产组并非是授权列表中授权的 授权用户的资产组这里的资产组并非是授权列表中授权的
@ -134,7 +150,22 @@ class UserGroupGrantedAssetsApi(ListAPIView):
return queryset return queryset
class UserGroupGrantedNodeApi(ListAPIView): class UserGroupGrantedNodesApi(ListAPIView):
permission_classes = (IsSuperUser,)
serializer_class = NodeSerializer
def get_queryset(self):
group_id = self.kwargs.get('pk', '')
queryset = []
if group_id:
group = get_object_or_404(UserGroup, id=group_id)
nodes = NodePermissionUtil.get_user_group_nodes(group)
queryset = nodes.keys()
return queryset
class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
serializer_class = NodeGrantedSerializer serializer_class = NodeGrantedSerializer

View File

@ -4,7 +4,7 @@
from users.utils import AdminUserRequiredMixin from users.utils import AdminUserRequiredMixin
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset, AssetGroup, SystemUser, Node from assets.models import Asset, AssetGroup, SystemUser, Node
from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer, NodeSerializer

View File

@ -11,12 +11,17 @@ router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permi
urlpatterns = [ urlpatterns = [
# 查询某个用户授权的资产和资产组 # 查询某个用户授权的资产和资产组
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})?/?assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'), url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})?/?nodes/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes'), url(r'^v1/user/assets/$', api.UserGrantedAssetsApi.as_view(), name='my-assets'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$', api.UserGrantedNodesApi.as_view(), name='user-nodes'),
url(r'^v1/user/nodes/$', api.UserGrantedNodesApi.as_view(), name='my-nodes'),
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
url(r'^v1/user/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='my-nodes-assets'),
# 查询某个用户组授权的资产和资产组 # 查询某个用户组授权的资产和资产组
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'), url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$', api.UserGroupGrantedNodeApi.as_view(), name='user-group-asset-groups'), url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$', api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'),
# 验证用户是否有某个资产和系统用户的权限 # 验证用户是否有某个资产和系统用户的权限
url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'), url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'),

View File

@ -3,15 +3,12 @@
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings
from django.views.generic import ListView, CreateView, UpdateView from django.views.generic import ListView, CreateView, UpdateView
from django.views.generic.edit import DeleteView from django.views.generic.edit import DeleteView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic.detail import DetailView, SingleObjectMixin
from common.utils import get_object_or_none from common.utils import get_object_or_none
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \ from .hands import AdminUserRequiredMixin, Node
Asset, AssetGroup, Node
from .models import AssetPermission, NodePermission from .models import AssetPermission, NodePermission
from .forms import AssetPermissionForm from .forms import AssetPermissionForm
@ -74,85 +71,9 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
# class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
# template_name = 'perms/asset_permission_detail.html'
# context_object_name = 'asset_permission'
# model = AssetPermission
#
# def get_context_data(self, **kwargs):
# context = {
# 'app': _('Perms'),
# 'action': _('Asset permission detail'),
# 'system_users_remain': [
# system_user for system_user in SystemUser.objects.all()
# if system_user not in self.object.system_users.all()],
# 'system_users': self.object.system_users.all(),
# }
# kwargs.update(context)
# return super().get_context_data(**kwargs)
class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView): class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
model = AssetPermission model = AssetPermission
template_name = 'delete_confirm.html' template_name = 'delete_confirm.html'
success_url = reverse_lazy('perms:asset-permission-list') success_url = reverse_lazy('perms:asset-permission-list')
# class AssetPermissionUserView(AdminUserRequiredMixin,
# SingleObjectMixin,
# ListView):
# template_name = 'perms/asset_permission_user.html'
# context_object_name = 'asset_permission'
# paginate_by = settings.DISPLAY_PER_PAGE
# object = None
#
# def get(self, request, *args, **kwargs):
# self.object = self.get_object(queryset=AssetPermission.objects.all())
# return super().get(request, *args, **kwargs)
#
# def get_queryset(self):
# queryset = self.object.get_granted_users()
# return queryset
#
# def get_context_data(self, **kwargs):
# users_granted = self.get_queryset()
# groups_granted = self.object.user_groups.all()
# context = {
# 'app': _('Perms'),
# 'action': _('Asset permission user list'),
# 'users_remain': User.objects.exclude(id__in=[user.id for user in users_granted]),
# 'user_groups': self.object.user_groups.all(),
# 'user_groups_remain': UserGroup.objects.exclude(id__in=[group.id for group in groups_granted])
# }
# kwargs.update(context)
# return super().get_context_data(**kwargs)
# class AssetPermissionAssetView(AdminUserRequiredMixin,
# SingleObjectMixin,
# ListView):
# template_name = 'perms/asset_permission_asset.html'
# context_object_name = 'asset_permission'
# paginate_by = settings.DISPLAY_PER_PAGE
# object = None
#
# def get(self, request, *args, **kwargs):
# self.object = self.get_object(queryset=AssetPermission.objects.all())
# return super().get(request, *args, **kwargs)
#
# def get_queryset(self):
# queryset = self.object.get_granted_assets()
# return queryset
#
# def get_context_data(self, **kwargs):
# assets_granted = self.get_queryset()
# groups_granted = self.object.asset_groups.all()
# context = {
# 'app': _('Perms'),
# 'action': _('Asset permission asset list'),
# 'assets_remain': Asset.objects.exclude(id__in=[asset.id for asset in assets_granted]),
# 'asset_groups': self.object.asset_groups.all(),
# 'asset_groups_remain': AssetGroup.objects.exclude(id__in=[group.id for group in groups_granted])
# }
# kwargs.update(context)
# return super().get_context_data(**kwargs)

View File

@ -4,8 +4,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -15,77 +15,41 @@
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<a href="{% url 'users:user-detail' pk=user.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a> <a href="{% url 'users:user-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a>
</li> </li>
<li class="active"> <li class="active">
<a href="{% url 'users:user-granted-asset' pk=user.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a> <a href="{% url 'users:user-granted-asset' pk=object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-6" style="padding-left: 0;"> <div class="col-lg-3" style="padding-left: 0px">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-content mailbox-content" style="padding-top: 0">
<span style="float: left">{% trans 'Assets granted of ' %} <b>{{ user.name }}</b></span> <div class="file-manager ">
<div class="ibox-tools"> <div id="assetTree" class="ztree">
<a class="collapse-link"> </div>
<i class="fa fa-chevron-up"></i>
</a> <div class="clearfix"></div>
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> </div>
<i class="fa fa-wrench"></i> </div>
</a> </div>
<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="user_assets_table" >
<thead>
<tr>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'System user' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div> </div>
<div class="col-sm-6" style="padding-left: 0;padding-right: 0"> <div class="col-lg-9 animated fadeInRight">
<div class="ibox float-e-margins"> <div class="mail-box-header">
<div class="ibox-title"> <table class="table table-striped table-bordered table-hover" id="user_assets_table" >
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user.name }}</b></span> <thead>
<div class="ibox-tools"> <tr>
<a class="collapse-link"> <th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<i class="fa fa-chevron-up"></i> <th class="text-center">{% trans 'Hostname' %}</th>
</a> <th class="text-center">{% trans 'IP' %}</th>
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> <th class="text-center">{% trans 'Active' %}</th>
<i class="fa fa-wrench"></i> <th class="text-center">{% trans 'Reachable' %}</th>
</a> </tr>
<ul class="dropdown-menu dropdown-user"> </thead>
</ul> <tbody>
<a class="close-link"> </tbody>
<i class="fa fa-times"></i> </table>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover" id="user_asset_groups_table" >
<thead>
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -96,42 +60,114 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { var zTree;
$('.select2').select2(); var inited = false;
var options = {
ele: $('#user_assets_table'), function initTable() {
buttons: [], if (inited){
order: [], return
select: [], } else {
columnDefs: [ inited = true;
{targets: 0, createdCell: function (td, cellData, rowData) { }
var detail_btn = '<a href="{% url "assets:asset-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var options = {
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); ele: $('#user_assets_table'),
}} columnDefs: [
], {targets: 1, createdCell: function (td, cellData, rowData) {
ajax_url: '{% url "api-perms:user-assets" pk=user.id %}', {% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
columns: [{data: "hostname" }, {data: "ip" }, var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
{data: "system_users_join"} $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
] }},
}; {targets: 3, createdCell: function (td, cellData) {
var options2 = { if (!cellData) {
ele: $('#user_asset_groups_table'), $(td).html('<i class="fa fa-times text-danger"></i>')
buttons: [], } else {
order: [], $(td).html('<i class="fa fa-check text-navy"></i>')
select: [], }
columnDefs: [ }},
{targets: 0, createdCell: function (td, cellData, rowData) { {targets: 4, createdCell: function (td, cellData) {
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; if (cellData === 'Unknown'){
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html('<i class="fa fa-circle text-warning"></i>')
}} } else if (!cellData) {
], $(td).html('<i class="fa fa-circle text-danger"></i>')
ajax_url: '{% url "api-perms:user-asset-groups" pk=user.id %}', } else {
columns: [{data: "name" }, {data: "assets_amount" }], $(td).html('<i class="fa fa-circle text-navy"></i>')
paging: false }
}; }}
jumpserver.initDataTable(options); ],
jumpserver.initDataTable(options2); ajax_url: '{% url "api-assets:asset-list" %}',
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" },
{data: "is_active", orderable: false },
{data: "is_connective", orderable: false}
]
};
asset_table = jumpserver.initServerSideDataTable(options);
return asset_table
}
function onSelected(event, treeNode) {
initTable();
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
setCookie('node_selected', treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
function selectQueryNode() {
var query_node_id = $.getUrlParam("node");
var cookie_node_id = getCookie('node_selected');
var node;
var node_id;
if (query_node_id !== null) {
node_id = query_node_id
} else if (cookie_node_id !== null) {
node_id = cookie_node_id;
}
node = zTree.getNodesByParam("id", node_id, null);
if (node){
zTree.selectNode(node[0]);
}
}
function initTree() {
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onSelected: onSelected
}
};
var zNodes = [];
$.get("{% url 'api-perms:user-nodes' pk=object.id %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
}); });
</script> zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
}
$(document).ready(function () {
initTree();
});
</script>
{% endblock %} {% endblock %}

View File

@ -21,9 +21,9 @@
<li class="active"> <li class="active">
<a href="{% url 'users:user-group-detail' pk=user_group.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User group detail' %} </a> <a href="{% url 'users:user-group-detail' pk=user_group.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User group detail' %} </a>
</li> </li>
{# <li>#} <li>
{# <a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>#} <a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
{# </li>#} </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a> <a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>

View File

@ -4,8 +4,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet"> <link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -15,81 +15,41 @@
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<a href="{% url 'users:user-group-detail' pk=user_group.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User detail' %} </a> <a href="{% url 'users:user-group-detail' pk=object.id %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'User group detail' %} </a>
</li> </li>
<li class="active"> <li class="active">
<a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a> <a href="{% url 'users:user-group-granted-asset' pk=object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="tab-content"> <div class="tab-content">
<div class="col-sm-7" style="padding-left: 0;"> <div class="col-lg-3" style="padding-left: 0px">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-content mailbox-content" style="padding-top: 0">
<span style="float: left">{% trans 'Assets granted of ' %} <b>{{ user_group.name }}</b></span> <div class="file-manager ">
<div class="ibox-tools"> <div id="assetTree" class="ztree">
<a class="collapse-link"> </div>
<i class="fa fa-chevron-up"></i>
</a> <div class="clearfix"></div>
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> </div>
<i class="fa fa-wrench"></i> </div>
</a> </div>
<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="user_assets_table" >
<thead>
<tr>
<th class="text-center"></th>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'System user' %}</th>
<th>{% trans 'Valid' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div> </div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0"> <div class="col-lg-9 animated fadeInRight">
<div class="ibox float-e-margins"> <div class="mail-box-header">
<div class="ibox-title"> <table class="table table-striped table-bordered table-hover" id="user_assets_table" >
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user_group.name }}</b></span> <thead>
<div class="ibox-tools"> <tr>
<a class="collapse-link"> <th class="text-center"><input type="checkbox" class="ipt_check_all"></th>
<i class="fa fa-chevron-up"></i> <th class="text-center">{% trans 'Hostname' %}</th>
</a> <th class="text-center">{% trans 'IP' %}</th>
<a class="dropdown-toggle" data-toggle="dropdown" href="#"> <th class="text-center">{% trans 'Active' %}</th>
<i class="fa fa-wrench"></i> <th class="text-center">{% trans 'Reachable' %}</th>
</a> </tr>
<ul class="dropdown-menu dropdown-user"> </thead>
</ul> <tbody>
<a class="close-link"> </tbody>
<i class="fa fa-times"></i> </table>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table table-hover" id="user_asset_groups_table" >
<thead>
<tr>
<th></th>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Asset' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -100,54 +60,114 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { var zTree;
$('.select2').select2(); var inited = false;
var options = {
ele: $('#user_assets_table'), function initTable() {
buttons: [], if (inited){
order: [], return
select: [], } else {
columnDefs: [ inited = true;
{targets: 1, createdCell: function (td, cellData, rowData) { }
var detail_btn = '<a href="{% url "assets:asset-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var options = {
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); ele: $('#user_assets_table'),
}}, columnDefs: [
{targets: 4, createdCell: function (td, cellData, rowData) { {targets: 1, createdCell: function (td, cellData, rowData) {
if (cellData.length > 10){ {% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
$(td).html(cellData.substring(1, 10) + '..') var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
} else { $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
$(td).html(cellData) }},
} {targets: 3, createdCell: function (td, cellData) {
}}, if (!cellData) {
{targets: 5, createdCell: function (td, cellData) { $(td).html('<i class="fa fa-times text-danger"></i>')
if (!cellData) { } else {
$(td).html('<i class="fa fa-times text-danger"></i>') $(td).html('<i class="fa fa-check text-navy"></i>')
} else { }
$(td).html('<i class="fa fa-check text-navy"></i>') }},
} {targets: 4, createdCell: function (td, cellData) {
}} if (cellData === 'Unknown'){
], $(td).html('<i class="fa fa-circle text-warning"></i>')
ajax_url: '{% url "api-perms:user-group-assets" pk=user_group.id %}', } else if (!cellData) {
columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"}, $(td).html('<i class="fa fa-circle text-danger"></i>')
{data: "system_users_join"}, {data: "is_active"}] } else {
}; $(td).html('<i class="fa fa-circle text-navy"></i>')
var options2 = { }
ele: $('#user_asset_groups_table'), }}
buttons: [], ],
order: [], ajax_url: '{% url "api-assets:asset-list" %}',
select: [], columns: [
columnDefs: [ {data: "id"}, {data: "hostname" }, {data: "ip" },
{targets: 1, createdCell: function (td, cellData, rowData) { {data: "is_active", orderable: false },
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; {data: "is_connective", orderable: false}
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); ]
}} };
], asset_table = jumpserver.initServerSideDataTable(options);
ajax_url: '{% url "api-perms:user-group-asset-groups" pk=user_group.id %}', return asset_table
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }] }
};
jumpserver.initDataTable(options); function onSelected(event, treeNode) {
jumpserver.initDataTable(options2); initTable();
var url = asset_table.ajax.url();
url = setUrlParam(url, "node_id", treeNode.id);
setCookie('node_selected', treeNode.id);
asset_table.ajax.url(url);
asset_table.ajax.reload();
}
function selectQueryNode() {
var query_node_id = $.getUrlParam("node");
var cookie_node_id = getCookie('node_selected');
var node;
var node_id;
if (query_node_id !== null) {
node_id = query_node_id
} else if (cookie_node_id !== null) {
node_id = cookie_node_id;
}
node = zTree.getNodesByParam("id", node_id, null);
if (node){
zTree.selectNode(node[0]);
}
}
function initTree() {
var setting = {
view: {
dblClickExpand: false,
showLine: true
},
data: {
simpleData: {
enable: true
}
},
callback: {
onSelected: onSelected
}
};
var zNodes = [];
$.get("{% url 'api-perms:user-group-nodes' pk=object.id %}", function(data, status){
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
}); });
</script> zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
});
}
$(document).ready(function () {
initTree();
});
</script>
{% endblock %} {% endblock %}

View File

@ -88,10 +88,7 @@ class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = UserGroup model = UserGroup
template_name = 'users/user_group_granted_asset.html' template_name = 'users/user_group_granted_asset.html'
context_object_name = 'user_group' context_object_name = 'user_group'
object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=self.model.objects.all())
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {

View File

@ -295,10 +295,6 @@ class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
template_name = 'users/user_granted_asset.html' template_name = 'users/user_granted_asset.html'
object = None object = None
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Users'), 'app': _('Users'),