mirror of https://github.com/jumpserver/jumpserver
[Update] 使用资产树,去掉集群和资产组
parent
3bb6e08985
commit
f82d939d15
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from .asset import *
|
||||
from .label import *
|
||||
from .user import *
|
|
@ -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
|
|
@ -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
|
|
@ -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'),
|
||||
}
|
|
@ -28,19 +28,22 @@ def default_cluster():
|
|||
return cluster.id
|
||||
|
||||
|
||||
def default_node():
|
||||
from .tree import Node
|
||||
return Node.root()
|
||||
|
||||
|
||||
class Asset(models.Model):
|
||||
# Important
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
|
||||
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
|
||||
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
||||
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups'))
|
||||
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"))
|
||||
nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes"))
|
||||
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
|
||||
|
||||
# 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
|
||||
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
|
||||
|
|
|
@ -12,7 +12,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||
"""
|
||||
资产的数据结构
|
||||
"""
|
||||
cluster_name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Asset
|
||||
|
@ -27,10 +26,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||
])
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def get_cluster_name(obj):
|
||||
return obj.cluster.name
|
||||
|
||||
|
||||
class AssetGrantedSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
|
|
|
@ -34,8 +34,6 @@ class SystemUserSerializer(serializers.ModelSerializer):
|
|||
@staticmethod
|
||||
def get_assets_amount(obj):
|
||||
amount = 0
|
||||
for cluster in obj.cluster.all():
|
||||
amount += cluster.assets.all().count()
|
||||
return amount
|
||||
|
||||
|
||||
|
|
|
@ -39,91 +39,78 @@ def on_asset_created(sender, instance=None, created=False, **kwargs):
|
|||
logger.info("Asset `{}` create signal received".format(instance))
|
||||
update_asset_hardware_info_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 on_asset_init(sender, instance, created=False, **kwargs):
|
||||
if instance and created is False:
|
||||
instance.__original_cluster = instance.cluster
|
||||
# def push_to_cluster_assets_on_system_user_created_or_update(system_user):
|
||||
# if not system_user.auto_push:
|
||||
# return
|
||||
# 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)
|
||||
def on_asset_cluster_changed(sender, instance=None, created=False, **kwargs):
|
||||
if instance and created is False and instance.cluster != instance.__original_cluster:
|
||||
logger.info("Asset cluster changed signal received")
|
||||
push_cluster_system_users_to_asset(instance)
|
||||
|
||||
|
||||
def push_to_cluster_assets_on_system_user_created_or_update(system_user):
|
||||
if not system_user.auto_push:
|
||||
return
|
||||
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=SystemUser)
|
||||
def on_system_user_created_or_updated(sender, instance=None, **kwargs):
|
||||
if instance and instance.auto_push:
|
||||
logger.info("System user `{}` create or update signal received".format(instance))
|
||||
push_to_cluster_assets_on_system_user_created_or_update(instance)
|
||||
|
||||
|
||||
@receiver(post_init, sender=Cluster, dispatch_uid="my_unique_identifier")
|
||||
def on_cluster_init(sender, instance, **kwargs):
|
||||
instance.__original_assets = tuple(instance.assets.values_list('pk', flat=True))
|
||||
instance.__origin_system_users = tuple(instance.systemuser_set.values_list('pk', flat=True))
|
||||
|
||||
|
||||
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier")
|
||||
def on_cluster_assets_changed(sender, instance, **kwargs):
|
||||
assets_origin = instance.__original_assets
|
||||
assets_new = instance.assets.values_list('pk', flat=True)
|
||||
assets_added = set(assets_new) - set(assets_origin)
|
||||
if assets_added:
|
||||
logger.debug("Receive cluster change assets signal")
|
||||
logger.debug("Push cluster `{}` system users to: {}".format(
|
||||
instance, ', '.join([str(asset) for asset in assets_added])
|
||||
))
|
||||
assets = []
|
||||
for asset_id in assets_added:
|
||||
try:
|
||||
asset = Asset.objects.get(pk=asset_id)
|
||||
except Asset.DoesNotExist:
|
||||
continue
|
||||
else:
|
||||
assets.append(asset)
|
||||
system_users = [s for s in instance.systemuser_set.all() if s.auto_push]
|
||||
task_name = _("Push system user to assets")
|
||||
push_system_user_util.delay(system_users, assets, task_name)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier2")
|
||||
def on_cluster_system_user_changed(sender, instance, **kwargs):
|
||||
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)
|
||||
)
|
||||
# @receiver(post_save, sender=SystemUser)
|
||||
# def on_system_user_created_or_updated(sender, instance=None, **kwargs):
|
||||
# if instance and instance.auto_push:
|
||||
# logger.info("System user `{}` create or update signal received".format(instance))
|
||||
# push_to_cluster_assets_on_system_user_created_or_update(instance)
|
||||
#
|
||||
#
|
||||
# @receiver(post_init, sender=Cluster, dispatch_uid="my_unique_identifier")
|
||||
# def on_cluster_init(sender, instance, **kwargs):
|
||||
# instance.__original_assets = tuple(instance.assets.values_list('pk', flat=True))
|
||||
# instance.__origin_system_users = tuple(instance.systemuser_set.values_list('pk', flat=True))
|
||||
#
|
||||
#
|
||||
# @receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier")
|
||||
# def on_cluster_assets_changed(sender, instance, **kwargs):
|
||||
# assets_origin = instance.__original_assets
|
||||
# assets_new = instance.assets.values_list('pk', flat=True)
|
||||
# assets_added = set(assets_new) - set(assets_origin)
|
||||
# if assets_added:
|
||||
# logger.debug("Receive cluster change assets signal")
|
||||
# logger.debug("Push cluster `{}` system users to: {}".format(
|
||||
# instance, ', '.join([str(asset) for asset in assets_added])
|
||||
# ))
|
||||
# assets = []
|
||||
# for asset_id in assets_added:
|
||||
# try:
|
||||
# asset = Asset.objects.get(pk=asset_id)
|
||||
# except Asset.DoesNotExist:
|
||||
# continue
|
||||
# else:
|
||||
# assets.append(asset)
|
||||
# system_users = [s for s in instance.systemuser_set.all() if s.auto_push]
|
||||
# task_name = _("Push system user to assets")
|
||||
# push_system_user_util.delay(system_users, assets, task_name)
|
||||
#
|
||||
#
|
||||
# @receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier2")
|
||||
# def on_cluster_system_user_changed(sender, instance, **kwargs):
|
||||
# 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)
|
||||
|
||||
|
|
|
@ -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 %}
|
|
@ -17,7 +17,6 @@
|
|||
{% bootstrap_field form.hostname layout="horizontal" %}
|
||||
{% bootstrap_field form.ip layout="horizontal" %}
|
||||
{% bootstrap_field form.port layout="horizontal" %}
|
||||
{% bootstrap_field form.cluster layout="horizontal" %}
|
||||
{% bootstrap_field form.public_ip layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
|
|
@ -1,75 +1,127 @@
|
|||
{% extends '_base_list.html' %}
|
||||
{% load i18n %}
|
||||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||
<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_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 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>
|
||||
|
||||
{% 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 %}
|
||||
<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>
|
||||
<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_list_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_foot_js %}
|
||||
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
<script>
|
||||
var zTree, rMenu, asset_table;
|
||||
function initTable() {
|
||||
var options = {
|
||||
ele: $('#asset_list_table'),
|
||||
|
@ -79,21 +131,18 @@ function initTable() {
|
|||
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) {
|
||||
{targets: 3, createdCell: function (td, cellData, rowData) {
|
||||
$(td).html(rowData.hardware_info)
|
||||
}},
|
||||
{targets: 6, createdCell: function (td, cellData) {
|
||||
{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: 7, createdCell: function (td, cellData) {
|
||||
if (cellData == 'Unknown'){
|
||||
{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>')
|
||||
|
@ -101,7 +150,7 @@ function initTable() {
|
|||
$(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 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)
|
||||
|
@ -109,25 +158,195 @@ function initTable() {
|
|||
],
|
||||
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||
columns: [
|
||||
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
|
||||
{data: "cluster"},
|
||||
{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()
|
||||
};
|
||||
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(){
|
||||
initTable();
|
||||
$(".select2").select2();
|
||||
initTree();
|
||||
})
|
||||
.on('click', '.labels li', function () {
|
||||
var val = $(this).text();
|
||||
$("#asset_list_table_filter input").val(val);
|
||||
jumpserver.table.search(val).draw();
|
||||
})
|
||||
asset_table.search(val).draw();
|
||||
})
|
||||
.on('click', '.btn_export', function () {
|
||||
var $data_table = $('#asset_list_table').DataTable();
|
||||
var rows = $data_table.rows('.selected').data();
|
||||
|
@ -167,7 +386,16 @@ $(document).ready(function(){
|
|||
}
|
||||
$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();
|
||||
|
@ -198,7 +426,7 @@ $(document).ready(function(){
|
|||
data.push(obj);
|
||||
});
|
||||
function success() {
|
||||
location.reload()
|
||||
asset_table.ajax.reload()
|
||||
}
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
|
@ -214,7 +442,7 @@ $(document).ready(function(){
|
|||
data.push(obj);
|
||||
});
|
||||
function success() {
|
||||
location.reload();
|
||||
asset_table.ajax.reload()
|
||||
}
|
||||
APIUpdateAttr({
|
||||
url: the_url,
|
||||
|
@ -258,6 +486,31 @@ $(document).ready(function(){
|
|||
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();
|
||||
|
@ -271,10 +524,13 @@ $(document).ready(function(){
|
|||
case 'active':
|
||||
doActive();
|
||||
break;
|
||||
case 'remove':
|
||||
doRemove();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
|
@ -22,7 +22,6 @@
|
|||
{% bootstrap_field form.hostname layout="horizontal" %}
|
||||
{% bootstrap_field form.ip layout="horizontal" %}
|
||||
{% bootstrap_field form.port layout="horizontal" %}
|
||||
{% bootstrap_field form.cluster layout="horizontal" %}
|
||||
{% bootstrap_field form.public_ip layout="horizontal" %}
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
|
|
|
@ -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 %}
|
|
@ -5,4 +5,4 @@ from .asset import *
|
|||
from .system_user import *
|
||||
from .admin_user import *
|
||||
from .label import *
|
||||
from .tree import *
|
||||
from .node import *
|
||||
|
|
|
@ -41,7 +41,7 @@ logger = get_logger(__file__)
|
|||
|
||||
|
||||
class AssetListView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'assets/tree.html'
|
||||
template_name = 'assets/asset_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
|
|
|
@ -10,7 +10,7 @@ from users.permissions import IsValidUser, IsSuperUser, IsSuperUserOrAppUser
|
|||
from .utils import NodePermissionUtil
|
||||
from .models import NodePermission
|
||||
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, \
|
||||
NodeGrantedSerializer, SystemUser
|
||||
NodeGrantedSerializer, SystemUser, NodeSerializer
|
||||
from . import serializers
|
||||
|
||||
|
||||
|
@ -30,8 +30,10 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
|||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
node_id = self.request.query_params.get('node_id')
|
||||
|
||||
if node_id:
|
||||
queryset = queryset.filter(node__id=node_id)
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
|
@ -62,6 +64,20 @@ class UserGrantedAssetsApi(ListAPIView):
|
|||
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):
|
||||
"""
|
||||
授权用户的资产组,注:这里的资产组并非是授权列表中授权的,
|
||||
|
@ -134,7 +150,22 @@ class UserGroupGrantedAssetsApi(ListAPIView):
|
|||
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,)
|
||||
serializer_class = NodeGrantedSerializer
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from users.utils import AdminUserRequiredMixin
|
||||
from users.models import User, UserGroup
|
||||
from assets.models import Asset, AssetGroup, SystemUser, Node
|
||||
from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer
|
||||
from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer, NodeSerializer
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -11,12 +11,17 @@ router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permi
|
|||
|
||||
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})?/?nodes/$', api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes'),
|
||||
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
|
||||
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})/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'),
|
||||
|
|
|
@ -3,15 +3,12 @@
|
|||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.conf import settings
|
||||
from django.views.generic import ListView, CreateView, UpdateView
|
||||
from django.views.generic.edit import DeleteView
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||
|
||||
from common.utils import get_object_or_none
|
||||
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \
|
||||
Asset, AssetGroup, Node
|
||||
from .hands import AdminUserRequiredMixin, Node
|
||||
from .models import AssetPermission, NodePermission
|
||||
from .forms import AssetPermissionForm
|
||||
|
||||
|
@ -74,85 +71,9 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
|
|||
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):
|
||||
model = AssetPermission
|
||||
template_name = 'delete_confirm.html'
|
||||
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)
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||
<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>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -15,77 +15,41 @@
|
|||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<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 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>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div class="col-sm-6" style="padding-left: 0;">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'Assets granted of ' %} <b>{{ user.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</a>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table table-hover " 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 class="col-lg-3" style="padding-left: 0px">
|
||||
<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-sm-6" style="padding-left: 0;padding-right: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</a>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table table-hover" id="user_asset_groups_table" >
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Asset' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-9 animated fadeInRight">
|
||||
<div class="mail-box-header">
|
||||
<table class="table table-striped table-bordered table-hover" id="user_assets_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 'Active' %}</th>
|
||||
<th class="text-center">{% trans 'Reachable' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -96,42 +60,114 @@
|
|||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
var options = {
|
||||
ele: $('#user_assets_table'),
|
||||
buttons: [],
|
||||
order: [],
|
||||
select: [],
|
||||
columnDefs: [
|
||||
{targets: 0, createdCell: function (td, cellData, rowData) {
|
||||
var detail_btn = '<a href="{% url "assets:asset-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||
}}
|
||||
],
|
||||
ajax_url: '{% url "api-perms:user-assets" pk=user.id %}',
|
||||
columns: [{data: "hostname" }, {data: "ip" },
|
||||
{data: "system_users_join"}
|
||||
]
|
||||
};
|
||||
var options2 = {
|
||||
ele: $('#user_asset_groups_table'),
|
||||
buttons: [],
|
||||
order: [],
|
||||
select: [],
|
||||
columnDefs: [
|
||||
{targets: 0, createdCell: function (td, cellData, rowData) {
|
||||
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||
}}
|
||||
],
|
||||
ajax_url: '{% url "api-perms:user-asset-groups" pk=user.id %}',
|
||||
columns: [{data: "name" }, {data: "assets_amount" }],
|
||||
paging: false
|
||||
};
|
||||
jumpserver.initDataTable(options);
|
||||
jumpserver.initDataTable(options2);
|
||||
<script>
|
||||
var zTree;
|
||||
var inited = false;
|
||||
|
||||
function initTable() {
|
||||
if (inited){
|
||||
return
|
||||
} else {
|
||||
inited = true;
|
||||
}
|
||||
var options = {
|
||||
ele: $('#user_assets_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) {
|
||||
if (!cellData) {
|
||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
||||
} else {
|
||||
$(td).html('<i class="fa fa-check text-navy"></i>')
|
||||
}
|
||||
}},
|
||||
{targets: 4, createdCell: function (td, cellData) {
|
||||
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>')
|
||||
}
|
||||
}}
|
||||
],
|
||||
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 %}
|
||||
|
|
|
@ -21,9 +21,9 @@
|
|||
<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>
|
||||
</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>#}
|
||||
{# </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>
|
||||
</li>
|
||||
<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>
|
||||
</li>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block custom_head_css_js %}
|
||||
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
|
||||
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
|
||||
<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>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="wrapper wrapper-content animated fadeInRight">
|
||||
|
@ -15,81 +15,41 @@
|
|||
<div class="panel-options">
|
||||
<ul class="nav nav-tabs">
|
||||
<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 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>
|
||||
<li class="active">
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div class="col-sm-7" style="padding-left: 0;">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'Assets granted of ' %} <b>{{ user_group.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</a>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table table-hover " id="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 class="col-lg-3" style="padding-left: 0px">
|
||||
<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-sm-5" style="padding-left: 0;padding-right: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'Asset groups granted of ' %} <b>{{ user_group.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</a>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table table-hover" id="user_asset_groups_table" >
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Asset' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-lg-9 animated fadeInRight">
|
||||
<div class="mail-box-header">
|
||||
<table class="table table-striped table-bordered table-hover" id="user_assets_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 'Active' %}</th>
|
||||
<th class="text-center">{% trans 'Reachable' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -100,54 +60,114 @@
|
|||
|
||||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
var options = {
|
||||
ele: $('#user_assets_table'),
|
||||
buttons: [],
|
||||
order: [],
|
||||
select: [],
|
||||
columnDefs: [
|
||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||
var detail_btn = '<a href="{% url "assets:asset-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||
}},
|
||||
{targets: 4, createdCell: function (td, cellData, rowData) {
|
||||
if (cellData.length > 10){
|
||||
$(td).html(cellData.substring(1, 10) + '..')
|
||||
} else {
|
||||
$(td).html(cellData)
|
||||
}
|
||||
}},
|
||||
{targets: 5, createdCell: function (td, cellData) {
|
||||
if (!cellData) {
|
||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
||||
} else {
|
||||
$(td).html('<i class="fa fa-check text-navy"></i>')
|
||||
}
|
||||
}}
|
||||
],
|
||||
ajax_url: '{% url "api-perms:user-group-assets" pk=user_group.id %}',
|
||||
columns: [{data: function(){return ""}}, {data: "hostname" }, {data: "ip" }, {data: "port"},
|
||||
{data: "system_users_join"}, {data: "is_active"}]
|
||||
};
|
||||
var options2 = {
|
||||
ele: $('#user_asset_groups_table'),
|
||||
buttons: [],
|
||||
order: [],
|
||||
select: [],
|
||||
columnDefs: [
|
||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||
var detail_btn = '<a href="{% url "assets:asset-group-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
|
||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
||||
}}
|
||||
],
|
||||
ajax_url: '{% url "api-perms:user-group-asset-groups" pk=user_group.id %}',
|
||||
columns: [{data: function(){return ""}}, {data: "name" }, {data: "assets_amount" }]
|
||||
};
|
||||
jumpserver.initDataTable(options);
|
||||
jumpserver.initDataTable(options2);
|
||||
<script>
|
||||
var zTree;
|
||||
var inited = false;
|
||||
|
||||
function initTable() {
|
||||
if (inited){
|
||||
return
|
||||
} else {
|
||||
inited = true;
|
||||
}
|
||||
var options = {
|
||||
ele: $('#user_assets_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) {
|
||||
if (!cellData) {
|
||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
||||
} else {
|
||||
$(td).html('<i class="fa fa-check text-navy"></i>')
|
||||
}
|
||||
}},
|
||||
{targets: 4, createdCell: function (td, cellData) {
|
||||
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>')
|
||||
}
|
||||
}}
|
||||
],
|
||||
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-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 %}
|
||||
|
|
|
@ -88,10 +88,7 @@ class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
|||
model = UserGroup
|
||||
template_name = 'users/user_group_granted_asset.html'
|
||||
context_object_name = 'user_group'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=self.model.objects.all())
|
||||
return super().get(request, *args, **kwargs)
|
||||
object = None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
|
|
|
@ -295,10 +295,6 @@ class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
|||
template_name = 'users/user_granted_asset.html'
|
||||
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):
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
|
|
Loading…
Reference in New Issue