Commit 7f3d32a8 authored by q4speed's avatar q4speed

Merge remote-tracking branch 'origin/dev' into dev

parents 6d7b5968 b2b123b4
......@@ -26,7 +26,7 @@ from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \
get_user_granted_assets
from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser
from . import serializers
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_util, \
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \
test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \
test_system_user_connectability_manual
......@@ -40,18 +40,14 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
"""
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsValidUser,)
permission_classes = (IsSuperUserOrAppUser,)
def get_queryset(self):
if self.request.user.is_superuser or self.request.user.is_app:
queryset = super().get_queryset()
else:
assets_granted = get_user_granted_assets(self.request.user)
queryset = self.queryset.filter(id__in=[asset.id for asset in assets_granted])
queryset = super().get_queryset()
cluster_id = self.request.query_params.get('cluster_id')
asset_group_id = self.request.query_params.get('asset_group_id')
admin_user_id = self.request.query_params.get('admin_user_id')
system_user_id = self.request.query_params.get('system_user_id')
if cluster_id:
queryset = queryset.filter(cluster__id=cluster_id)
......@@ -62,6 +58,23 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
assets_direct = [asset.id for asset in admin_user.asset_set.all()]
clusters = [cluster.id for cluster in admin_user.cluster_set.all()]
queryset = queryset.filter(Q(cluster__id__in=clusters)|Q(id__in=assets_direct))
if system_user_id:
system_user = get_object_or_404(SystemUser, id=system_user_id)
clusters = system_user.get_clusters()
queryset = queryset.filter(cluster__in=clusters)
return queryset
class UserAssetListView(generics.ListAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsValidUser,)
def get_queryset(self):
assets_granted = get_user_granted_assets(self.request.user)
queryset = self.queryset.filter(
id__in=[asset.id for asset in assets_granted]
)
return queryset
......@@ -99,15 +112,6 @@ class GroupAddAssetsApi(generics.UpdateAPIView):
return Response({'error': serializer.errors}, status=400)
class ClusterUpdateAssetsApi(generics.RetrieveUpdateAPIView):
"""
Cluster update asset member
"""
queryset = Cluster.objects.all()
serializer_class = serializers.ClusterUpdateAssetsSerializer
permission_classes = (IsSuperUser,)
class ClusterViewSet(IDInFilterMixin, BulkModelViewSet):
"""
Cluster api set, for add,delete,update,list,retrieve resource
......@@ -117,7 +121,6 @@ class ClusterViewSet(IDInFilterMixin, BulkModelViewSet):
permission_classes = (IsSuperUser,)
# TOdo
class ClusterTestAssetsAliveApi(generics.RetrieveAPIView):
"""
Test cluster asset can connect using admin user or not
......@@ -127,6 +130,9 @@ class ClusterTestAssetsAliveApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
cluster = self.get_object()
admin_user = cluster.admin_user
test_admin_user_connectability_manual.delay(admin_user)
return Response("Task has been send, seen left assets status")
class ClusterAddAssetsApi(generics.UpdateAPIView):
......@@ -256,7 +262,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
admin_user = self.get_object()
test_admin_user_connectability_util.delay(admin_user)
test_admin_user_connectability_manual.delay(admin_user)
return Response({"msg": "Task created"})
......
......@@ -36,7 +36,7 @@ class AssetCreateForm(forms.ModelForm):
def clean_admin_user(self):
cluster = self.cleaned_data.get('cluster')
admin_user = self.cleaned_data.get('admin_user')
if not cluster.admin_user and not 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']
......@@ -64,7 +64,7 @@ class AssetUpdateForm(forms.ModelForm):
def clean_admin_user(self):
cluster = self.cleaned_data.get('cluster')
admin_user = self.cleaned_data.get('admin_user')
if not cluster.admin_user and not 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']
......@@ -124,20 +124,25 @@ class AssetGroupForm(forms.ModelForm):
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
def __init__(self, **kwargs):
instance = kwargs.get('instance')
if instance:
initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all()
super(AssetGroupForm, self).__init__(*args, **kwargs)
initial.update({
'assets': instance.assets.all(),
})
kwargs['initial'] = initial
super().__init__(**kwargs)
def _save_m2m(self):
super(AssetGroupForm, self)._save_m2m()
assets = self.cleaned_data['assets']
self.instance.assets.clear()
self.instance.assets.add(*tuple(assets))
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
......@@ -150,10 +155,19 @@ class AssetGroupForm(forms.ModelForm):
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',
fields = ['name', "bandwidth", "operator", 'contact', 'admin_user', 'system_users',
'phone', 'address', 'intranet', 'extranet', 'comment']
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
......@@ -162,9 +176,21 @@ class ClusterForm(forms.ModelForm):
}
help_texts = {
'name': '* required',
'admin_user': 'The assets of this cluster will use this admin user as his admin user',
'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 AdminUserForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it,
......@@ -172,9 +198,10 @@ class AdminUserForm(forms.ModelForm):
widget=forms.PasswordInput, max_length=128,
strip=True, required=False,
help_text=_('Password or private key password'),
label=_("Password"),
)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
private_key_file = forms.FileField(required=False, label=_("Private key"))
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
......@@ -204,12 +231,14 @@ class AdminUserForm(forms.ModelForm):
return private_key_file
def clean(self):
super().clean()
password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '')
if not self.instance and not (password or private_key_file):
raise forms.ValidationError(
_('Password and private key file must be input one'))
if not password and not private_key_file:
raise forms.ValidationError(_(
'Password and private key file must be input one'
))
class Meta:
model = AdminUser
......@@ -229,9 +258,10 @@ class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=128, strip=True)
password = forms.CharField(widget=forms.PasswordInput, required=False,
max_length=128, strip=True, label=_("Password"))
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
private_key_file = forms.FileField(required=False, label=_("Private key"))
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
......@@ -278,15 +308,18 @@ class SystemUserForm(forms.ModelForm):
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'cluster': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _(' Select clusters')}),
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',
'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'),
}
......
......@@ -18,6 +18,16 @@ __all__ = ['Asset']
logger = logging.getLogger(__name__)
def default_cluster():
from .cluster import Cluster
name = "Default"
defaults = {"name": name}
cluster, created = Cluster.objects.get_or_create(
defaults=defaults, name=name
)
return cluster.id
class Asset(models.Model):
# Todo: Move them to settings
STATUS_CHOICES = (
......@@ -44,7 +54,7 @@ class Asset(models.Model):
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, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('Cluster'))
cluster = models.ForeignKey(Cluster, related_name='assets', default=default_cluster, on_delete=models.SET_DEFAULT, verbose_name=_('Cluster'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),)
env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),)
......@@ -158,6 +168,7 @@ class Asset(models.Model):
class Meta:
unique_together = ('ip', 'port')
verbose_name = _("Asset")
@classmethod
def generate_fake(cls, count=100):
......
......@@ -37,6 +37,7 @@ class Cluster(models.Model):
class Meta:
ordering = ['name']
verbose_name = _("Cluster")
@classmethod
def generate_fake(cls, count=5):
......
......@@ -27,6 +27,7 @@ class AssetGroup(models.Model):
class Meta:
ordering = ['name']
verbose_name = _("Asset group")
@classmethod
def initial(cls):
......
......@@ -81,7 +81,11 @@ class AssetUser(models.Model):
@property
def public_key(self):
return signer.unsign(self._public_key)
key = signer.unsign(self._public_key)
if key:
return key
else:
return None
@property
def public_key_obj(self):
......@@ -170,7 +174,6 @@ class AdminUser(AssetUser):
info = None
return info
def get_related_assets(self):
assets = []
for cluster in self.cluster_set.all():
......@@ -184,6 +187,7 @@ class AdminUser(AssetUser):
class Meta:
ordering = ['name']
verbose_name = _("Admin user")
@classmethod
def generate_fake(cls, count=10):
......@@ -224,7 +228,7 @@ class SystemUser(AssetUser):
def get_clusters_assets(self):
from .asset import Asset
clusters = self.cluster.all()
clusters = self.get_clusters()
return Asset.objects.filter(cluster__in=clusters)
def get_clusters(self):
......@@ -262,6 +266,7 @@ class SystemUser(AssetUser):
class Meta:
ordering = ['name']
verbose_name = _("System user")
@classmethod
def generate_fake(cls, count=10):
......
......@@ -64,6 +64,7 @@ class AdminUserSerializer(serializers.ModelSerializer):
"""
assets_amount = serializers.SerializerMethodField()
unreachable_amount = serializers.SerializerMethodField()
reachable_amount = serializers.SerializerMethodField()
class Meta:
model = AdminUser
......@@ -75,7 +76,15 @@ class AdminUserSerializer(serializers.ModelSerializer):
if data:
return len(data.get('dark'))
else:
return 'Unknown'
return 0
@staticmethod
def get_reachable_amount(obj):
data = cache.get(ADMIN_USER_CONN_CACHE_KEY.format(obj.name))
if data:
return len(data.get('contacted'))
else:
return 0
@staticmethod
def get_assets_amount(obj):
......@@ -183,7 +192,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
class Meta(object):
model = Asset
fields = ("id", "hostname", "ip", "port", "system_users_granted",
"is_inherited", "is_active", "system_users_join",
"is_inherited", "is_active", "system_users_join", "os",
"platform", "comment",)
@staticmethod
......
......@@ -27,16 +27,17 @@ def test_asset_conn_on_created(asset):
def push_cluster_system_users_to_asset(asset):
logger.info("Push cluster system user to asset: {}".format(asset))
task_name = _("Push cluster system users to asset")
system_users = asset.cluster.systemuser_set.all()
push_system_user_util.delay(system_users, [asset], task_name)
if asset.cluster:
logger.info("Push cluster system user to asset: {}".format(asset))
task_name = _("Push cluster system users to asset")
system_users = asset.cluster.systemuser_set.all()
push_system_user_util.delay(system_users, [asset], task_name)
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
def on_asset_created(sender, instance=None, created=False, **kwargs):
if instance and created:
logger.info("Asset `` create signal received".format(instance))
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)
......@@ -77,7 +78,7 @@ def on_system_user_created_or_updated(sender, instance=None, **kwargs):
@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.all())
instance.__origin_system_users = tuple(instance.systemuser_set.values_list('pk', flat=True))
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier")
......@@ -103,11 +104,11 @@ def on_cluster_assets_changed(sender, instance, **kwargs):
push_system_user_util.delay(system_users, assets, task_name)
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier")
@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) - system_users_origin
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 = []
......
......@@ -25,7 +25,7 @@ disk_pattern = re.compile(r'^hd|sd|xvd')
@shared_task
def set_assets_hardware_info(result, **kwargs):
"""
Unsing ops task run result, to update asset info
Using ops task run result, to update asset info
@shared_task must be exit, because we using it as a task callback, is must
be a celery task also
......@@ -209,8 +209,11 @@ def test_asset_connectability_util(asset, task_name=None):
from ops.utils import update_or_create_ansible_task
if task_name is None:
task_name = "Test asset connectability"
task_name = _("Test asset connectability")
hosts = [asset.hostname]
if not hosts:
logger.info("No hosts, passed")
return {}
tasks = const.TEST_ADMIN_USER_CONN_TASKS
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=hosts, tasks=tasks, pattern='all',
......@@ -262,6 +265,9 @@ def test_system_user_connectability_util(system_user, task_name):
assets = system_user.get_clusters_assets()
hosts = [asset.hostname for asset in assets]
tasks = const.TEST_SYSTEM_USER_CONN_TASKS
if not hosts:
logger.info("No hosts, passed")
return {}
task, created = update_or_create_ansible_task(
task_name, hosts=hosts, tasks=tasks, pattern='all',
options=const.TASK_OPTIONS,
......@@ -274,7 +280,7 @@ def test_system_user_connectability_util(system_user, task_name):
@shared_task
def test_system_user_connectability_manual(system_user):
task_name = "Test system user connectability: {}".format(system_user)
task_name = _("Test system user connectability: {}").format(system_user)
return test_system_user_connectability_util(system_user, task_name)
......@@ -303,6 +309,10 @@ def test_system_user_connectability_period():
#### Push system user tasks ####
def get_push_system_user_tasks(system_user):
# Set root as system user is dangerous
if system_user.username == "root":
return []
tasks = [
{
'name': 'Add user {}'.format(system_user.username),
......@@ -310,7 +320,7 @@ def get_push_system_user_tasks(system_user):
'module': 'user',
'args': 'name={} shell={} state=present password={}'.format(
system_user.username, system_user.shell,
encrypt_password(system_user.password),
encrypt_password(system_user.password, salt="K3mIlKK"),
),
}
},
......@@ -346,11 +356,14 @@ def push_system_user_util(system_users, assets, task_name):
for system_user in system_users:
tasks.extend(get_push_system_user_tasks(system_user))
print("Task: ", tasks)
if not tasks:
return
logger.info("Not tasks, passed")
return {}
hosts = [asset.hostname for asset in assets]
if not hosts:
logger.info("Not hosts, passed")
return {}
task, created = update_or_create_ansible_task(
task_name=task_name, hosts=hosts, tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, run_as_admin=True, created_by='System'
......@@ -381,8 +394,8 @@ def push_system_user_period():
for system_user in system_users:
tasks.extend(get_push_system_user_tasks(system_user))
task_name = _("Push system user to cluster assets period: {}->{}").format(
cluster.name, ', '.join(s.name for s in system_users)
task_name = _("Push cluster system users to assets period: {}").format(
cluster.name
)
hosts = [asset.hostname for asset in cluster.assets.all()]
update_or_create_ansible_task(
......
......@@ -3,8 +3,8 @@
{% load static %}
{% load bootstrap3 %}
{% 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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
......
......@@ -21,11 +21,11 @@
<a href="{% url 'assets:admin-user-assets' pk=admin_user.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Assets list' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-admin-user">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
......@@ -60,7 +60,7 @@
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Type' %}</th>
<th>{% trans 'Alive' %}</th>
<th>{% trans 'Reachable' %}</th>
</tr>
</thead>
<tbody>
......@@ -144,7 +144,7 @@ $(document).ready(function () {
url: the_url,
error: error,
method: 'GET',
success_message: "{% trans "Task has been send, seen left asset status" %}"
success_message: "{% trans 'Task has been send, seen left asset status' %}"
});
})
</script>
......
......@@ -3,8 +3,8 @@
{% load static %}
{% load bootstrap3 %}
{% 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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
......
......@@ -21,11 +21,11 @@
<a href="{% url 'assets:admin-user-assets' pk=admin_user.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Assets list' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-admin-user">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
......@@ -133,7 +133,6 @@ function bindToCluster(clusters) {
$('.select2-selection__rendered').empty();
$('#cluster_selected').val('');
$.map(jumpserver.cluster_selected, function(cluster_name, index) {
console.log(index);
$('#opt_' + index).remove();
// change tr html of user groups.
$('#table-clusters tbody').append(
......
......@@ -21,8 +21,10 @@
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th>
<th class="text-center">{% trans 'Unreachable' %}</th>
<th class="text-center">{% trans 'Ratio' %}</th>
<th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
......@@ -41,17 +43,50 @@ $(document).ready(function(){
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
}},
{targets: 4, createdCell: function (td, cellData) {
var innerHtml = "";
if (cellData !== 0) {
innerHtml = "<span class='text-navy'>" + cellData + "</span>";
} else {
innerHtml = "<span>" + cellData + "</span>";
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData +'">' + innerHtml + '</span>');
}},
{targets: 5, createdCell: function (td, cellData) {
var innerHtml = "";
if (cellData !== 0) {
innerHtml = "<span class='text-danger'>" + cellData + "</span>";
} else {
innerHtml = "<span>" + cellData + "</span>";
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 6, createdCell: function (td, cellData, rowData) {
{# var script_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);#}
var val = 0;
var innerHtml = "";
var total = rowData.assets_amount;
var reachable = rowData.reachable_amount;
if (total !== 0) {
val = reachable/total * 100;
}
if (val === 100) {
innerHtml = "<span class='text-navy'>" + val + "% </span>";
} else {
innerHtml = "<span class='text-danger'>" + val + "% </span>";
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 8, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
{# $(td).html(script_btn + update_btn + del_btn)#}
$(td).html(update_btn + del_btn)
}}],
ajax_url: '{% url "api-assets:admin-user-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" },
{data: "unreachable_amount"}, {data: "comment" }, {data: "id" }]
{data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" }]
};
jumpserver.initDataTable(options);
})
......
......@@ -21,11 +21,11 @@
</li>
{% if user.is_superuser %}
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-asset">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
{% endif %}
......
{% extends 'base.html' %}
{% load i18n %}
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> {{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
{% load i18n %}
<div class="ibox-content">
<div class="panel blank-panel">
<div class="panel-body">
<div class="tab-content">
<form id="groupForm" method="post" class="form-horizontal">
{% csrf_token %}
<h3 class="widget-head-color-box">资产组信息</h3>
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
{# <div class="hr-line-dashed"></div>#}
{# <h3 class="widget-head-color-box">用户选择的资产</h3>#}
{# <div class="form-group">#}
{# <label class="col-sm-2 control-label" id="asset_on_count">已选({{ assets_count }})</label>#}
{# <div class="col-sm-9" id="asset_sed">#}
{# <div class="form-asset-on" id="add_asset">#}
{# <p id="asset_on_p">#}
{# {% for asset in assets_on_list %}#}
{# <button name='asset_hostname' title='{{ asset.ip }}' type='button' class='btn btn-default btn-xs'>{{ asset.hostname }}</button>#}
{# {% endfor %}#}
{# </p>#}
{# </div>#}
{# </div>#}
{# </div>#}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-5">
<button class="btn btn-white" type="reset"> 重置 </button>
<button class="btn btn-primary" type="submit"> 提交 </button>
<div id='box2'> </div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% block form %}
<form id="groupForm" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.assets layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</div>
<!-- 模态框(Modal) -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" id="box">
<!--此部分为主体内容,将远程加载进来-->
</div>
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2();
$('.select2-system-user').select2();
});
$('#add_asset').on('click',function(){
$('#modal').modal('show');
});
$('#modal').modal({
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}"
});
$('#modal').on('show.bs.modal',function(){
//alert('当调用show方法时,立即触发;')
$('.select2').select2({
closeOnSelect: false
});
});
$('#modal').on('shown.bs.modal',function(){
//alert('当弹窗完全加载完后,再触发;')
});
$('#modal').on('hide.bs.modal',function(){
//alert('当关闭时,立即触发;')
});
$('#modal').on('hidden.bs.modal',function(){
//alert('当关完全关闭后,再触发;')
});
$('#modal').on('loaded.bs.modal',function(){
//alert('当远程数据加载完毕后,再触发;')
});
</script>
{% endblock %}
\ No newline at end of file
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -15,7 +15,12 @@
<ul class="nav nav-tabs">
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Group assets' %} </a></li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-group-update' pk=asset_group.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-group-update' pk=asset_group.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-del">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
</div>
......@@ -212,7 +217,6 @@ $(document).ready(function () {
addAssets(assets_id);
})
.on('click', '.btn-leave-group', function () {
var $this = $(this);
var the_url = "{% url 'api-assets:group-update-assets' pk=asset_group.id %}";
......@@ -223,9 +227,15 @@ $(document).ready(function () {
});
var delete_asset_id = $(this).data('aid');
assets.remove(delete_asset_id);
console.log(assets);
var data = {"assets": assets};
leaveGroup($this, name, the_url, data);
}).on('click', '.btn-del', function () {
var $this = $(this);
var name = "{{ asset_group.name}}";
var uid = "{{ asset_group.id }}";
var the_url = '{% url "api-assets:asset-group-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var redirect_url = "{% url 'assets:asset-group-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
......
......@@ -75,8 +75,6 @@ $(document).ready(function(){
return false;
}
var the_url = '{% url "api-assets:asset-group-list" %}';
console.log(plain_id_list);
console.log(the_url);
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
......
......@@ -109,7 +109,7 @@ function initTable() {
$(document).ready(function(){
initTable();
})
.on('click', '#btn_export', function () {
.on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data();
var assets = [];
......@@ -129,7 +129,7 @@ $(document).ready(function(){
}
})
})
.on('click', '#btn_import', function () {
.on('click', '#btn_asset_import', function () {
var $form = $('#fm_asset_import');
$form.find('.help-block').remove();
function success (data) {
......
......@@ -46,7 +46,6 @@
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.status layout="horizontal" %}
{% bootstrap_field form.env layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %}
......
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style type="text/css">
</style>
......@@ -94,7 +94,7 @@
<td colspan="2">
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for asset in assets_remain %}
<option value="{{ asset.id }}" id="opt_{{ asset.id }}">{{ asset.ip}}:{{ asset.port }}</option>
<option value="{{ asset.id }}" id="opt_{{ asset.id }}">{{ asset.hostname}}</option>
{% endfor %}
</select>
</td>
......@@ -204,7 +204,12 @@ $(document).ready(function () {
addAssets(assets_id);
})
.on('click', '#btn-test-assets', function () {
console.log('ok');
var the_url = "{% url 'api-assets:cluster-test-connective' pk=cluster.id %}";
APIUpdateAttr({
url: the_url,
method: 'GET',
success_message: "{% trans 'Task has been send, seen left assets status' %}"
});
})
</script>
{% endblock %}
......@@ -3,8 +3,8 @@
{% load static %}
{% load bootstrap3 %}
{% 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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -40,6 +40,7 @@
<h3 class="widget-head-color-box">{% trans 'Settings' %}</h3>
{% bootstrap_field form.admin_user layout="horizontal" %}
{% bootstrap_field form.system_users layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">{% trans 'Other' %}</h3>
......@@ -69,10 +70,7 @@
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2({
dropdownAutoWidth : true,
width: 'auto'
});
})
$('.select2').select2();
});
</script>
{% endblock %}
\ No newline at end of file
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -22,11 +22,11 @@
</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:cluster-update' pk=cluster.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:cluster-update' pk=cluster.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-cluster">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
......
......@@ -8,7 +8,7 @@
{% block table_search %}{% endblock %}
{% block table_container %}
<div class="uc pull-left m-r-5">
<a href="{% url "assets:cluster-create" %}" class="btn btn-sm btn-primary"> {% trans "Create Cluster" %} </a>
<a href="{% url "assets:cluster-create" %}" class="btn btn-sm btn-primary"> {% trans "Create cluster" %} </a>
</div>
<table class="table table-striped table-bordered table-hover " id="cluster_list_table" >
<thead>
......
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -22,7 +22,7 @@
</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
</ul>
</div>
......
......@@ -23,7 +23,12 @@
</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-del">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
</div>
......@@ -259,6 +264,13 @@ $(document).ready(function () {
return $(this).data('gid');
}).get();
updateSystemUserCluster(clusters);
}).on('click', '.btn-del', function () {
var $this = $(this);
var name = "{{ system_user.name}}";
var uid = "{{ system_user.id }}";
var the_url = '{% url "api-assets:system-user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var redirect_url = "{% url 'assets:system-user-list' %}";
objectDelete($this, name, the_url, redirect_url);
})
</script>
{% endblock %}
......@@ -42,7 +42,6 @@ function initTable() {
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
console.log('{{ the_url }}');
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
......@@ -67,7 +66,7 @@ function initTable() {
$(td).html(conn_btn)
}}
],
ajax_url: '{% url "api-assets:asset-list" %}',
ajax_url: '{% url "api-assets:user-asset-list" %}',
columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},
......
......@@ -21,16 +21,18 @@ urlpatterns = [
api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'),
url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/alive/$',
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
url(r'^v1/assets/user-assets/$',
api.UserAssetListView.as_view(), name='user-asset-list'),
# update the asset group, which add or delete the asset to the group
url(r'^v1/groups/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.GroupUpdateAssetsApi.as_view(), name='group-update-assets'),
url(r'^v1/groups/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$',
api.GroupAddAssetsApi.as_view(), name='group-add-assets'),
# update the Cluster, and add or delete the assets to the Cluster
url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.ClusterUpdateAssetsApi.as_view(), name='cluster-update-assets'),
url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.ClusterAddAssetsApi.as_view(), name='cluster-add-assets'),
url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/connective/$',
api.ClusterTestAssetsAliveApi.as_view(), name='cluster-test-connective'),
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/clusters/$',
api.AdminUserAddClustersApi.as_view(), name='admin-user-add-clusters'),
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$',
......
......@@ -2,20 +2,22 @@
from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext as _
from django.conf import settings
from django.views.generic import TemplateView, ListView, View
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
from django.views.generic import TemplateView, ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from common.const import create_success_msg, update_success_msg
from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..models import AdminUser, Cluster
from ..hands import AdminUserRequiredMixin
__all__ = ['AdminUserCreateView', 'AdminUserDetailView',
'AdminUserDeleteView', 'AdminUserListView',
'AdminUserUpdateView', 'AdminUserAssetsView',
]
__all__ = [
'AdminUserCreateView', 'AdminUserDetailView',
'AdminUserDeleteView', 'AdminUserListView',
'AdminUserUpdateView', 'AdminUserAssetsView',
]
class AdminUserListView(AdminUserRequiredMixin, TemplateView):
......@@ -38,6 +40,7 @@ class AdminUserCreateView(AdminUserRequiredMixin,
form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html'
success_url = reverse_lazy('assets:admin-user-list')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -47,20 +50,13 @@ class AdminUserCreateView(AdminUserRequiredMixin,
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _(
'Create admin user <a href="{url}">{name}</a> successfully.'.format(
url=reverse_lazy('assets:admin-user-detail',
kwargs={'pk': self.object.pk}),
name=self.object.name,
))
return success_message
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
class AdminUserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = AdminUser
form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html'
success_url = reverse_lazy('assets:admin-user-list')
success_message = update_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -70,11 +66,6 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:admin-user-detail',
kwargs={'pk': self.object.pk})
return success_url
class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
model = AdminUser
......
......@@ -21,10 +21,11 @@ from django.core.cache import cache
from django.utils import timezone
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect
from django.contrib.messages.views import SuccessMessageMixin
from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger, is_uuid
from common.const import create_success_msg, update_success_msg
from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin
......@@ -46,7 +47,6 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
context = {
'app': _('Assets'),
'action': _('Asset list'),
# 'groups': AssetGroup.objects.all(),
'system_users': SystemUser.objects.all(),
}
kwargs.update(context)
......@@ -66,7 +66,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView):
return super().get_context_data(**kwargs)
class AssetCreateView(AdminUserRequiredMixin, CreateView):
class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = Asset
form_class = forms.AssetCreateForm
template_name = 'assets/asset_create.html'
......@@ -87,6 +87,9 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return create_success_msg % ({"name": cleaned_data["hostname"]})
class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......@@ -147,7 +150,7 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView):
return super().get_context_data(**kwargs)
class AssetUpdateView(AdminUserRequiredMixin, UpdateView):
class AssetUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = Asset
form_class = forms.AssetUpdateForm
template_name = 'assets/asset_update.html'
......@@ -159,7 +162,10 @@ class AssetUpdateView(AdminUserRequiredMixin, UpdateView):
'action': _('Update asset'),
}
kwargs.update(context)
return super(AssetUpdateView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return update_success_msg % ({"name": cleaned_data["hostname"]})
class AssetDeleteView(AdminUserRequiredMixin, DeleteView):
......@@ -184,14 +190,14 @@ class AssetDetailView(DetailView):
'system_users_all': SystemUser.objects.all(),
}
kwargs.update(context)
return super(AssetDetailView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
@method_decorator(csrf_exempt, name='dispatch')
class AssetExportView(View):
def get(self, request):
spm = request.GET.get('spm', '')
assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else [1]
assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else []
assets_id = cache.get(spm, assets_id_default)
fields = [
field for field in Asset._meta.fields
......
# coding:utf-8
from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext as _
from django.views.generic import TemplateView, ListView, View
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.contrib.messages.views import SuccessMessageMixin
from common.const import create_success_msg, update_success_msg
from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin
__all__ = ['ClusterListView', 'ClusterCreateView', 'ClusterUpdateView',
'ClusterDetailView', 'ClusterDeleteView', 'ClusterAssetsView']
__all__ = [
'ClusterListView', 'ClusterCreateView', 'ClusterUpdateView',
'ClusterDetailView', 'ClusterDeleteView', 'ClusterAssetsView',
]
class ClusterListView(AdminUserRequiredMixin, TemplateView):
......@@ -21,39 +25,40 @@ class ClusterListView(AdminUserRequiredMixin, TemplateView):
context = {
'app': _('Assets'),
'action': _('Cluster list'),
# 'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class ClusterCreateView(AdminUserRequiredMixin, CreateView):
class ClusterCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = Cluster
form_class = forms.ClusterForm
template_name = 'assets/cluster_create_update.html'
success_url = reverse_lazy('assets:cluster-list')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('assets'),
'action': _('Create Cluster'),
'action': _('Create cluster'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def form_valid(self, form):
cluster = form.save(commit=False)
cluster.created_by = self.request.user.username or 'System'
cluster.created_by = self.request.user.username
cluster.save()
return super().form_valid(form)
class ClusterUpdateView(AdminUserRequiredMixin, UpdateView):
class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = Cluster
form_class = forms.ClusterForm
template_name = 'assets/cluster_create_update.html'
context_object_name = 'cluster'
success_url = reverse_lazy('assets:cluster-list')
success_message = update_success_msg
def form_valid(self, form):
cluster = form.save(commit=False)
......
......@@ -7,42 +7,41 @@ from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateVi
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.shortcuts import get_object_or_404, reverse, redirect
from django.contrib.messages.views import SuccessMessageMixin
from common.const import create_success_msg, update_success_msg
from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin
__all__ = ['AssetGroupCreateView', 'AssetGroupDetailView',
'AssetGroupUpdateView', 'AssetGroupListView',
'AssetGroupDeleteView',
]
__all__ = [
'AssetGroupCreateView', 'AssetGroupDetailView',
'AssetGroupUpdateView', 'AssetGroupListView',
'AssetGroupDeleteView',
]
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView):
class AssetGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AssetGroup
form_class = forms.AssetGroupForm
template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create asset group'),
'assets_count': 0,
}
kwargs.update(context)
return super(AssetGroupCreateView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
def form_valid(self, form):
asset_group = form.save()
assets_id_list = self.request.POST.getlist('assets', [])
assets = [get_object_or_404(Asset, id=int(asset_id))
for asset_id in assets_id_list]
asset_group.created_by = self.request.user.username or 'Admin'
asset_group.assets.add(*tuple(assets))
asset_group.save()
return super(AssetGroupCreateView, self).form_valid(form)
group = form.save()
group.created_by = self.request.user.username
group.save()
return super().form_valid(form)
class AssetGroupListView(AdminUserRequiredMixin, TemplateView):
......@@ -54,7 +53,6 @@ class AssetGroupListView(AdminUserRequiredMixin, TemplateView):
'action': _('Asset group list'),
'assets': Asset.objects.all(),
'system_users': SystemUser.objects.all(),
'keyword': self.request.GET.get('keyword', '')
}
kwargs.update(context)
return super(AssetGroupListView, self).get_context_data(**kwargs)
......@@ -77,27 +75,20 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
return super().get_context_data(**kwargs)
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView):
class AssetGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = AssetGroup
form_class = forms.AssetGroupForm
template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list')
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetGroup.objects.all())
return super(AssetGroupUpdateView, self).get(request, *args, **kwargs)
success_message = update_success_msg
def get_context_data(self, **kwargs):
assets_all = self.object.assets.all()
context = {
'app': _('Assets'),
'action': _('Create asset group'),
'assets_on_list': assets_all,
'assets_count': len(assets_all),
'group_id': self.object.id,
}
kwargs.update(context)
return super(AssetGroupUpdateView, self).get_context_data(**kwargs)
return super().get_context_data(**kwargs)
class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):
......
# ~*~ coding: utf-8 ~*~
from django.contrib import messages
from django.shortcuts import redirect, reverse
from django.utils.translation import ugettext as _
from django.db import transaction
from django.views.generic import TemplateView, ListView, FormView
from django.views.generic import TemplateView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.detail import DetailView
from ..forms import SystemUserForm, SystemUserUpdateForm, SystemUserAuthForm
from common.const import create_success_msg, update_success_msg
from ..forms import SystemUserForm, SystemUserUpdateForm
from ..models import SystemUser, Cluster
from ..hands import AdminUserRequiredMixin
__all__ = ['SystemUserCreateView', 'SystemUserUpdateView',
'SystemUserDetailView', 'SystemUserDeleteView',
'SystemUserAssetView', 'SystemUserListView',
]
__all__ = [
'SystemUserCreateView', 'SystemUserUpdateView',
'SystemUserDetailView', 'SystemUserDeleteView',
'SystemUserAssetView', 'SystemUserListView',
]
class SystemUserListView(AdminUserRequiredMixin, TemplateView):
......@@ -38,10 +37,7 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
form_class = SystemUserForm
template_name = 'assets/system_user_create.html'
success_url = reverse_lazy('assets:system-user-list')
@transaction.atomic
def post(self, request, *args, **kwargs):
return super(SystemUserCreateView, self).post(request, *args, **kwargs)
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -51,20 +47,13 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse('assets:system-user-detail', kwargs={'pk': self.object.pk})
success_message = _(
'Create system user <a href="{url}">{name}</a> '
'successfully.'.format(url=url, name=self.object.name)
)
return success_message
class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
class SystemUserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = SystemUser
form_class = SystemUserUpdateForm
template_name = 'assets/system_user_update.html'
success_url = reverse_lazy('assets:system-user-list')
success_message = update_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -74,11 +63,6 @@ class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:system-user-detail',
kwargs={'pk': self.object.pk})
return success_url
class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'assets/system_user_detail.html'
......@@ -109,8 +93,8 @@ class SystemUserAssetView(AdminUserRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = {
'app': 'assets',
'action': 'System user asset',
'app': _('assets'),
'action': _('System user asset'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext as _
create_success_msg = _("<b>%(name)s</b> was created successfully")
update_success_msg = _("<b>%(name)s</b> was updated successfully")
\ No newline at end of file
......@@ -287,10 +287,10 @@ def make_signature(access_key_secret, date=None):
return content_md5(data)
def encrypt_password(password):
def encrypt_password(password, salt=None):
from passlib.hash import sha512_crypt
if password:
return sha512_crypt.using(rounds=5000).hash(password)
return sha512_crypt.using(rounds=5000).hash(password, salt=salt)
return None
......
......@@ -4,24 +4,44 @@ import os
import re
import pytz
from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
DEMO_MODE = os.environ.get("DEMO_MODE", "")
SAFE_URL = r'^/users/login|^/api/terminal/v1/.*|/api/terminal/.*|/api/users/v1/auth/|/api/users/v1/profile/'
class TimezoneMiddleware:
def __init__(self, get_response):
self.get_response = get_response
class TimezoneMiddleware(MiddlewareMixin):
def process_request(self, request):
def __call__(self, request):
tzname = request.META.get('TZ')
if tzname:
timezone.activate(pytz.timezone(tzname))
else:
timezone.deactivate()
response = self.get_response(request)
return response
class DemoMiddleware:
DEMO_MODE_ENABLED = os.environ.get("DEMO_MODE", "") in ("1", "ok", "True")
SAFE_URL_PATTERN = re.compile(
r'^/users/login|'
r'^/api/terminal/v1/.*|'
r'^/api/terminal/.*|'
r'^/api/users/v1/auth/|'
r'^/api/users/v1/profile/'
)
SAFE_METHOD = ("GET", "HEAD")
def __init__(self, get_response):
self.get_response = get_response
class DemoMiddleware(MiddlewareMixin):
def process_request(self, request):
if DEMO_MODE and request.method not in ["GET", "HEAD"] and not re.match(SAFE_URL, request.path):
return HttpResponse("Demo mode, only get request accept", status=403)
if self.DEMO_MODE_ENABLED:
print("Demo mode enabled, reject unsafe method and url")
def __call__(self, request):
if self.DEMO_MODE_ENABLED and request.method not in self.SAFE_METHOD \
and not self.SAFE_URL_PATTERN.match(request.path):
return HttpResponse("Demo mode, only safe request accepted", status=403)
else:
response = self.get_response(request)
return response
......@@ -240,9 +240,8 @@ LOGGING = {
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-cn'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
......@@ -300,7 +299,8 @@ REST_FRAMEWORK = {
'users.authentication.SessionAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z',
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'],
}
AUTHENTICATION_BACKENDS = [
......@@ -374,4 +374,5 @@ BOOTSTRAP3 = {
'horizontal_field_class': 'col-md-9',
# Set placeholder attributes to label if no placeholder is provided
'set_placeholder': True,
'success_css_class': '',
}
......@@ -36,6 +36,6 @@ urlpatterns = [
if settings.DEBUG:
urlpatterns += [
url(r'^docs/', schema_view, name="docs"),
] + static(settings.STATIC_URL, document_root=settings.STATIC_DIR) \
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-01-01 15:33+0800\n"
"POT-Creation-Date: 2018-01-10 00:30+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
......@@ -17,8 +17,8 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:36
#: perms/templates/perms/asset_permission_asset.html:127 users/forms.py:240
#: assets/forms.py:23 assets/forms.py:53 assets/forms.py:99 perms/forms.py:37
#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:242
msgid "Select asset groups"
msgstr "选择资产组"
......@@ -36,19 +36,19 @@ msgstr "主机级别管理用户,如果没有设置则默认使用集群级别
#: assets/forms.py:40 assets/forms.py:68
msgid "You need set a admin user if cluster not have"
msgstr ""
msgstr "集群没有管理用户,你需要为集群设置管理用户或设置一个主机级别的管理用户"
#: assets/forms.py:54
msgid "Default using cluster admin user"
msgstr "默认使用管理用户"
#: assets/forms.py:76 assets/forms.py:81 assets/forms.py:127
#: assets/templates/assets/asset_group_detail.html:70 perms/forms.py:33
#: perms/templates/perms/asset_permission_asset.html:99 users/forms.py:237
#: assets/templates/assets/asset_group_detail.html:70 perms/forms.py:34
#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:239
msgid "Select assets"
msgstr "选择资产"
#: assets/forms.py:86 assets/models/asset.py:45
#: assets/forms.py:86 assets/models/asset.py:55
#: assets/templates/assets/admin_user_assets.html:61
#: assets/templates/assets/asset_detail.html:69
#: assets/templates/assets/asset_group_detail.html:47
......@@ -56,12 +56,13 @@ msgstr "选择资产"
#: assets/templates/assets/cluster_assets.html:53
#: assets/templates/assets/system_user_asset.html:54
#: assets/templates/assets/user_asset_list.html:21
#: perms/templates/perms/asset_permission_asset.html:56
#: users/templates/users/user_group_granted_asset.html:51
msgid "Port"
msgstr "端口"
#: assets/forms.py:124 assets/templates/assets/asset_group_list.html:16
#: assets/forms.py:124 assets/models/asset.py:171
#: assets/templates/assets/admin_user_list.html:24
#: assets/templates/assets/asset_group_list.html:16
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17
#: perms/templates/perms/asset_permission_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:28 templates/_nav.html:22
......@@ -75,7 +76,22 @@ msgstr "端口"
msgid "Asset"
msgstr "资产"
#: assets/forms.py:159 assets/forms.py:219 assets/forms.py:278
#: assets/forms.py:161 perms/forms.py:40
#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:245
msgid "Select system users"
msgstr "选择系统用户"
#: assets/forms.py:163
#: assets/templates/assets/_asset_group_bulk_update_modal.html:22
#: assets/templates/assets/cluster_list.html:22
msgid "System users"
msgstr "系统用户"
#: assets/forms.py:165
msgid "Selected system users will be create at cluster assets"
msgstr "选择的系统用户将会在该集群资产上创建"
#: assets/forms.py:173 assets/forms.py:248 assets/forms.py:308
#: assets/models/cluster.py:18 assets/models/group.py:20
#: assets/models/user.py:28 assets/templates/assets/admin_user_detail.html:56
#: assets/templates/assets/admin_user_list.html:22
......@@ -84,13 +100,13 @@ msgstr "资产"
#: assets/templates/assets/cluster_list.html:19
#: assets/templates/assets/system_user_detail.html:53
#: assets/templates/assets/system_user_list.html:24 ops/models.py:31
#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:39
#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:34
#: perms/models.py:14
#: perms/templates/perms/asset_permission_create_update.html:33
#: perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:25
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:14
#: terminal/models.py:117 terminal/templates/terminal/terminal_detail.html:43
#: terminal/models.py:118 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:36 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:62
......@@ -104,19 +120,37 @@ msgstr "资产"
msgid "Name"
msgstr "名称"
#: assets/forms.py:174
#: assets/forms.py:179
msgid "Cluster level admin user"
msgstr "集群级别管理用户"
#: assets/forms.py:200
msgid "Password or private key password"
msgstr "密码或秘钥不合法"
#: assets/forms.py:202 assets/forms.py:260 assets/forms.py:321
#: assets/forms.py:201 assets/forms.py:262 assets/models/user.py:30
#: users/forms.py:16 users/forms.py:24 users/templates/users/login.html:56
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11
#: users/templates/users/user_password_update.html:40
#: users/templates/users/user_profile_update.html:40
#: users/templates/users/user_pubkey_update.html:40
msgid "Password"
msgstr "密码"
#: assets/forms.py:204 assets/forms.py:264 users/models/user.py:46
msgid "Private key"
msgstr "ssh私钥"
#: assets/forms.py:229 assets/forms.py:290 assets/forms.py:354
msgid "Invalid private key"
msgstr "ssh密钥不合法"
#: assets/forms.py:212
#: assets/forms.py:240
msgid "Password and private key file must be input one"
msgstr "密码和私钥, 必须输入一个"
#: assets/forms.py:220 assets/forms.py:279 assets/models/user.py:29
#: assets/forms.py:249 assets/forms.py:309 assets/models/user.py:29
#: assets/templates/assets/admin_user_detail.html:60
#: assets/templates/assets/admin_user_list.html:23
#: assets/templates/assets/system_user_detail.html:57
......@@ -132,59 +166,73 @@ msgstr "密码和私钥, 必须输入一个"
msgid "Username"
msgstr "用户名"
#: assets/forms.py:267 assets/forms.py:327
#: assets/forms.py:297 assets/forms.py:360
msgid "Auth info required, private_key or password"
msgstr "密钥和密码必须填写一个"
#: assets/forms.py:282
#: assets/forms.py:313
msgid " Select clusters"
msgstr "选择集群"
#: assets/models/asset.py:24
#: assets/forms.py:320
msgid "If auto push checked, system user will be create at cluster assets"
msgstr "如果选择了自动推送,系统用户将会创建在集群资产上"
#: assets/forms.py:321
msgid "Auto push system user to asset"
msgstr "自动推送系统用户到资产"
#: assets/forms.py:322
msgid ""
"High level will be using login asset as default, if user was granted more "
"than 2 system user"
msgstr "高优先级的系统用户将会作为默认登录用户"
#: assets/models/asset.py:34
msgid "In use"
msgstr "使用中"
#: assets/models/asset.py:25
#: assets/models/asset.py:35
msgid "Out of use"
msgstr "未使用"
#: assets/models/asset.py:28
#: assets/models/asset.py:38
msgid "Server"
msgstr "物理机"
#: assets/models/asset.py:29
#: assets/models/asset.py:39
msgid "VM"
msgstr "虚拟机"
#: assets/models/asset.py:30
#: assets/models/asset.py:40
msgid "Switch"
msgstr "交换机"
#: assets/models/asset.py:31
#: assets/models/asset.py:41
msgid "Router"
msgstr "路由器"
#: assets/models/asset.py:32
#: assets/models/asset.py:42
msgid "Firewall"
msgstr "防火墙"
#: assets/models/asset.py:33
#: assets/models/asset.py:43
msgid "Storage"
msgstr "存储"
#: assets/models/asset.py:36
#: assets/models/asset.py:46
msgid "Production"
msgstr "生产环境"
#: assets/models/asset.py:37
#: assets/models/asset.py:47
msgid "Development"
msgstr "开发环境"
#: assets/models/asset.py:38
#: assets/models/asset.py:48
msgid "Testing"
msgstr "测试环境"
#: assets/models/asset.py:43 assets/templates/assets/admin_user_assets.html:60
#: assets/models/asset.py:53 assets/templates/assets/admin_user_assets.html:60
#: assets/templates/assets/asset_detail.html:61
#: assets/templates/assets/asset_group_detail.html:46
#: assets/templates/assets/asset_list.html:31
......@@ -198,7 +246,7 @@ msgstr "测试环境"
msgid "IP"
msgstr "IP"
#: assets/models/asset.py:44 assets/templates/assets/admin_user_assets.html:59
#: assets/models/asset.py:54 assets/templates/assets/admin_user_assets.html:59
#: assets/templates/assets/asset_detail.html:57
#: assets/templates/assets/asset_group_detail.html:45
#: assets/templates/assets/asset_list.html:30
......@@ -211,116 +259,116 @@ msgstr "IP"
msgid "Hostname"
msgstr "主机名"
#: assets/models/asset.py:46 assets/templates/assets/asset_detail.html:213
#: assets/views/asset.py:212 assets/views/asset.py:252
#: assets/models/asset.py:56 assets/templates/assets/asset_detail.html:213
#: assets/views/asset.py:218 assets/views/asset.py:258
msgid "Asset groups"
msgstr "资产组"
#: assets/models/asset.py:47 assets/models/user.py:215
#: assets/templates/assets/asset_detail.html:85
#: assets/models/asset.py:57 assets/models/cluster.py:40
#: assets/models/user.py:219 assets/templates/assets/asset_detail.html:85
#: assets/templates/assets/asset_list.html:33 templates/_nav.html:24
msgid "Cluster"
msgstr "集群"
#: assets/models/asset.py:48 assets/templates/assets/asset_detail.html:129
#: assets/models/asset.py:58 assets/templates/assets/asset_detail.html:129
msgid "Is active"
msgstr "激活"
#: assets/models/asset.py:49 assets/templates/assets/asset_detail.html:133
#: assets/models/asset.py:59 assets/templates/assets/asset_detail.html:133
msgid "Asset type"
msgstr "系统类型"
#: assets/models/asset.py:50 assets/templates/assets/asset_detail.html:137
#: assets/models/asset.py:60 assets/templates/assets/asset_detail.html:137
msgid "Asset environment"
msgstr "资产环境"
#: assets/models/asset.py:51 assets/templates/assets/asset_detail.html:125
#: assets/models/asset.py:61 assets/templates/assets/asset_detail.html:125
msgid "Asset status"
msgstr "资产状态"
#: assets/models/asset.py:54 assets/models/cluster.py:19
#: assets/templates/assets/asset_detail.html:73
#: assets/models/asset.py:64 assets/models/cluster.py:19
#: assets/models/user.py:190 assets/templates/assets/asset_detail.html:73
#: assets/templates/assets/cluster_list.html:20 templates/_nav.html:25
msgid "Admin user"
msgstr "管理用户"
#: assets/models/asset.py:57 assets/templates/assets/asset_detail.html:65
#: assets/models/asset.py:67 assets/templates/assets/asset_detail.html:65
msgid "Public IP"
msgstr "公网IP"
#: assets/models/asset.py:58
#: assets/models/asset.py:68
msgid "Remote control card IP"
msgstr "远控卡IP"
#: assets/models/asset.py:59 assets/templates/assets/asset_detail.html:89
#: assets/models/asset.py:69 assets/templates/assets/asset_detail.html:89
msgid "Cabinet number"
msgstr "机柜编号"
#: assets/models/asset.py:60 assets/templates/assets/asset_detail.html:93
#: assets/models/asset.py:70 assets/templates/assets/asset_detail.html:93
msgid "Cabinet position"
msgstr "机柜层号"
#: assets/models/asset.py:61 assets/templates/assets/asset_detail.html:145
#: assets/models/asset.py:71 assets/templates/assets/asset_detail.html:145
msgid "Asset number"
msgstr "资产编号"
#: assets/models/asset.py:64 assets/templates/assets/asset_detail.html:97
#: assets/models/asset.py:74 assets/templates/assets/asset_detail.html:97
msgid "Vendor"
msgstr "制造商"
#: assets/models/asset.py:65 assets/templates/assets/asset_detail.html:101
#: assets/models/asset.py:75 assets/templates/assets/asset_detail.html:101
msgid "Model"
msgstr "型号"
#: assets/models/asset.py:66 assets/templates/assets/asset_detail.html:141
#: assets/models/asset.py:76 assets/templates/assets/asset_detail.html:141
msgid "Serial number"
msgstr "序列号"
#: assets/models/asset.py:68
#: assets/models/asset.py:78
msgid "CPU model"
msgstr "CPU型号"
#: assets/models/asset.py:69
#: assets/models/asset.py:79
msgid "CPU count"
msgstr "CPU数量"
#: assets/models/asset.py:70
#: assets/models/asset.py:80
msgid "CPU cores"
msgstr "CPU核数"
#: assets/models/asset.py:71 assets/templates/assets/asset_detail.html:109
#: assets/models/asset.py:81 assets/templates/assets/asset_detail.html:109
msgid "Memory"
msgstr "内存"
#: assets/models/asset.py:72
#: assets/models/asset.py:82
msgid "Disk total"
msgstr "硬盘大小"
#: assets/models/asset.py:73
#: assets/models/asset.py:83
msgid "Disk info"
msgstr "硬盘信息"
#: assets/models/asset.py:75 assets/templates/assets/asset_detail.html:117
#: assets/models/asset.py:85 assets/templates/assets/asset_detail.html:117
msgid "Platform"
msgstr "系统平台"
#: assets/models/asset.py:76 assets/templates/assets/asset_detail.html:121
#: assets/models/asset.py:86 assets/templates/assets/asset_detail.html:121
msgid "OS"
msgstr "操作系统"
#: assets/models/asset.py:77
#: assets/models/asset.py:87
msgid "OS version"
msgstr "系统版本"
#: assets/models/asset.py:78
#: assets/models/asset.py:88
msgid "OS arch"
msgstr "系统架构"
#: assets/models/asset.py:79
#: assets/models/asset.py:89
msgid "Hostname raw"
msgstr "主机名原始"
#: assets/models/asset.py:81 assets/models/cluster.py:28
#: assets/models/asset.py:91 assets/models/cluster.py:28
#: assets/models/group.py:21 assets/models/user.py:36
#: assets/templates/assets/admin_user_detail.html:68
#: assets/templates/assets/asset_detail.html:149
......@@ -332,7 +380,7 @@ msgstr "主机名原始"
msgid "Created by"
msgstr "创建者"
#: assets/models/asset.py:82 assets/models/cluster.py:26
#: assets/models/asset.py:92 assets/models/cluster.py:26
#: assets/models/group.py:22 assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/cluster_detail.html:89
#: assets/templates/assets/system_user_detail.html:87
......@@ -343,10 +391,10 @@ msgstr "创建者"
msgid "Date created"
msgstr "创建日期"
#: assets/models/asset.py:83 assets/models/cluster.py:29
#: assets/models/asset.py:93 assets/models/cluster.py:29
#: assets/models/group.py:23 assets/models/user.py:33
#: assets/templates/assets/admin_user_detail.html:72
#: assets/templates/assets/admin_user_list.html:26
#: assets/templates/assets/admin_user_list.html:28
#: assets/templates/assets/asset_detail.html:157
#: assets/templates/assets/asset_group_list.html:17
#: assets/templates/assets/cluster_detail.html:97
......@@ -391,11 +439,11 @@ msgstr "外网"
msgid "Operator"
msgstr "运营商"
#: assets/models/cluster.py:36 assets/models/group.py:33
#: assets/models/cluster.py:36 assets/models/group.py:34
msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 users/models/user.py:259
#: assets/models/cluster.py:36 users/models/user.py:263
msgid "System"
msgstr "系统"
......@@ -403,20 +451,15 @@ msgstr "系统"
msgid "Default Cluster"
msgstr "默认Cluster"
#: assets/models/group.py:33
#: assets/models/group.py:30 perms/models.py:18
#: perms/templates/perms/asset_permission_list.html:29 templates/_nav.html:23
msgid "Asset group"
msgstr "资产组"
#: assets/models/group.py:34
msgid "Default asset group"
msgstr "默认资产组"
#: assets/models/user.py:30 users/forms.py:16 users/forms.py:24
#: users/templates/users/login.html:56
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11
#: users/templates/users/user_password_update.html:40
#: users/templates/users/user_profile_update.html:40
#: users/templates/users/user_pubkey_update.html:40
msgid "Password"
msgstr "密码"
#: assets/models/user.py:31
msgid "SSH private key"
msgstr "ssh密钥"
......@@ -425,42 +468,55 @@ msgstr "ssh密钥"
msgid "SSH public key"
msgstr "ssh公钥"
#: assets/models/user.py:216
#: assets/models/user.py:220
msgid "Priority"
msgstr ""
msgstr "优先级"
#: assets/models/user.py:217 assets/templates/assets/system_user_detail.html:61
#: assets/models/user.py:221 assets/templates/assets/system_user_detail.html:61
msgid "Protocol"
msgstr "协议"
#: assets/models/user.py:218 assets/templates/assets/_system_user.html:59
#: assets/models/user.py:222 assets/templates/assets/_system_user.html:59
#: assets/templates/assets/system_user_detail.html:113
#: assets/templates/assets/system_user_update.html:11
msgid "Auto push"
msgstr "自动推送"
#: assets/models/user.py:219 assets/templates/assets/system_user_detail.html:65
#: assets/models/user.py:223 assets/templates/assets/system_user_detail.html:65
msgid "Sudo"
msgstr "Sudo"
#: assets/models/user.py:220 assets/templates/assets/system_user_detail.html:70
#: assets/models/user.py:224 assets/templates/assets/system_user_detail.html:70
msgid "Shell"
msgstr "Shell"
#: assets/models/user.py:269 perms/models.py:19
#: perms/templates/perms/asset_permission_detail.html:136
#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
#: terminal/backends/command/models.py:12 terminal/models.py:94
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49
#: terminal/templates/terminal/session_list.html:73
#: users/templates/users/user_granted_asset.html:50
#: users/templates/users/user_group_granted_asset.html:52
msgid "System user"
msgstr "系统用户"
#: assets/models/utils.py:29
#, python-format
msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
#: assets/signals_handler.py:31
#: assets/signals_handler.py:32
msgid "Push cluster system users to asset"
msgstr "推送集群系统用户到资产"
#: assets/signals_handler.py:63 assets/signals_handler.py:125
#: assets/signals_handler.py:64 assets/signals_handler.py:126
msgid "Push system user to cluster assets: {}->{}"
msgstr "推送系统用户到: {}->{}"
#: assets/signals_handler.py:102
#: assets/signals_handler.py:103
msgid "Push system user to assets"
msgstr "推送系统用户到资产"
......@@ -478,23 +534,31 @@ msgstr "定期更新资产硬件信息"
#: assets/tasks.py:189
msgid "Test admin user connectability period: {}"
msgstr "定期测试管理用户可以连接性"
msgstr "定期测试管理用户可连接性: {}"
#: assets/tasks.py:203
msgid "Test admin user connectability: {}"
msgstr "测试管理用户可连接性: {}"
#: assets/tasks.py:289
msgid "Test system user connectability period: {}"
#: assets/tasks.py:212
msgid "Test asset connectability"
msgstr "测试资产可连接性"
#: assets/tasks.py:283
msgid "Test system user connectability: {}"
msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:363
#: assets/tasks.py:295
msgid "Test system user connectability period: {}"
msgstr "定期测试系统用户可连接性: {}"
#: assets/tasks.py:376
msgid "Push system user to cluster assets: {}"
msgstr "推送系统用户到资产: {}"
#: assets/tasks.py:384
msgid "Push system user to cluster assets period: {}->{}"
msgstr "定期推送系统用户到资产: {}->{}"
#: assets/tasks.py:397
msgid "Push cluster system users to assets period: {}"
msgstr "定期推送集群系统用户到资产: {}"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:5
msgid "Update asset group"
......@@ -506,16 +570,16 @@ msgstr "仅修改你需要更新的字段"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:12
#: assets/templates/assets/system_user_asset.html:21
#: assets/views/admin_user.py:27 assets/views/admin_user.py:44
#: assets/views/admin_user.py:67 assets/views/admin_user.py:88
#: assets/views/admin_user.py:115 assets/views/asset.py:47
#: assets/views/asset.py:61 assets/views/asset.py:84 assets/views/asset.py:141
#: assets/views/asset.py:158 assets/views/asset.py:179
#: assets/views/cluster.py:22 assets/views/cluster.py:80
#: assets/views/cluster.py:97 assets/views/group.py:30 assets/views/group.py:53
#: assets/views/group.py:71 assets/views/group.py:93
#: assets/views/system_user.py:29 assets/views/system_user.py:48
#: assets/views/system_user.py:71 assets/views/system_user.py:91
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47
#: assets/views/admin_user.py:63 assets/views/admin_user.py:79
#: assets/views/admin_user.py:106 assets/views/asset.py:48
#: assets/views/asset.py:61 assets/views/asset.py:84 assets/views/asset.py:144
#: assets/views/asset.py:161 assets/views/asset.py:185
#: assets/views/cluster.py:26 assets/views/cluster.py:85
#: assets/views/cluster.py:102 assets/views/group.py:34
#: assets/views/group.py:52 assets/views/group.py:69 assets/views/group.py:87
#: assets/views/system_user.py:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:75
#: templates/_nav.html:19
msgid "Assets"
msgstr "资产管理"
......@@ -524,11 +588,6 @@ msgstr "资产管理"
msgid "Select Asset"
msgstr "选择资产"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:22
#: assets/templates/assets/cluster_list.html:22
msgid "System users"
msgstr "系统用户"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:24
msgid "Select System Users"
msgstr "选择系统用户"
......@@ -561,7 +620,7 @@ msgstr "如果设置了id,则会使用该行信息更新该id的资产"
#: assets/templates/assets/_system_user.html:16
#: assets/templates/assets/system_user_list.html:16
#: assets/views/system_user.py:49
#: assets/views/system_user.py:45
msgid "Create system user"
msgstr "创建系统用户"
......@@ -588,7 +647,7 @@ msgstr "自动生成秘钥"
#: assets/templates/assets/_system_user.html:65
#: assets/templates/assets/asset_create.html:32
#: assets/templates/assets/asset_update.html:47
#: assets/templates/assets/cluster_create_update.html:45
#: assets/templates/assets/cluster_create_update.html:46
#: perms/templates/perms/asset_permission_create_update.html:45
#: terminal/templates/terminal/terminal_update.html:40
msgid "Other"
......@@ -598,8 +657,9 @@ msgstr "其它"
#: assets/templates/assets/admin_user_create_update.html:45
#: assets/templates/assets/asset_bulk_update.html:23
#: assets/templates/assets/asset_create.html:40
#: assets/templates/assets/asset_update.html:56
#: assets/templates/assets/cluster_create_update.html:53
#: assets/templates/assets/asset_group_create.html:16
#: assets/templates/assets/asset_update.html:55
#: assets/templates/assets/cluster_create_update.html:54
#: perms/templates/perms/asset_permission_create_update.html:67
#: terminal/templates/terminal/terminal_update.html:45
#: users/templates/users/_user.html:49
......@@ -616,9 +676,10 @@ msgstr "重置"
#: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:41
#: assets/templates/assets/asset_group_create.html:17
#: assets/templates/assets/asset_list.html:55
#: assets/templates/assets/asset_update.html:57
#: assets/templates/assets/cluster_create_update.html:54
#: assets/templates/assets/asset_update.html:56
#: assets/templates/assets/cluster_create_update.html:55
#: perms/templates/perms/asset_permission_create_update.html:68
#: terminal/templates/terminal/terminal_update.html:46
#: users/templates/users/_user.html:50
......@@ -652,6 +713,52 @@ msgstr "详情"
msgid "Assets list"
msgstr "资产列表"
#: assets/templates/assets/admin_user_assets.html:24
#: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:83
#: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_group_detail.html:18
#: assets/templates/assets/asset_group_detail.html:172
#: assets/templates/assets/asset_group_list.html:42
#: assets/templates/assets/asset_list.html:95
#: assets/templates/assets/cluster_assets.html:170
#: assets/templates/assets/cluster_detail.html:25
#: assets/templates/assets/cluster_list.html:43
#: assets/templates/assets/system_user_asset.html:25
#: assets/templates/assets/system_user_detail.html:26
#: assets/templates/assets/system_user_list.html:84
#: perms/templates/perms/asset_permission_detail.html:30
#: perms/templates/perms/asset_permission_list.html:73
#: terminal/templates/terminal/terminal_detail.html:16
#: terminal/templates/terminal/terminal_list.html:71
#: users/templates/users/user_detail.html:25
#: users/templates/users/user_group_detail.html:28
#: users/templates/users/user_group_list.html:39
#: users/templates/users/user_list.html:76
msgid "Update"
msgstr "更新"
#: assets/templates/assets/admin_user_assets.html:28
#: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:84
#: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_group_list.html:43
#: assets/templates/assets/asset_list.html:96
#: assets/templates/assets/cluster_detail.html:29
#: assets/templates/assets/cluster_list.html:44
#: assets/templates/assets/system_user_list.html:85
#: ops/templates/ops/task_list.html:71
#: perms/templates/perms/asset_permission_detail.html:34
#: perms/templates/perms/asset_permission_list.html:74
#: terminal/templates/terminal/terminal_list.html:73
#: users/templates/users/user_detail.html:29
#: users/templates/users/user_group_detail.html:32
#: users/templates/users/user_group_list.html:41
#: users/templates/users/user_list.html:80
#: users/templates/users/user_list.html:84
msgid "Delete"
msgstr "删除"
#: assets/templates/assets/admin_user_assets.html:37
#: assets/templates/assets/asset_group_detail.html:26
#: perms/templates/perms/asset_permission_asset.html:35
......@@ -668,11 +775,13 @@ msgid "Type"
msgstr "类型"
#: assets/templates/assets/admin_user_assets.html:63
#: assets/templates/assets/asset_group_detail.html:49
#: assets/templates/assets/cluster_assets.html:55
#: terminal/templates/terminal/terminal_list.html:35
msgid "Alive"
msgstr "在线"
#: assets/templates/assets/admin_user_list.html:25
#: assets/templates/assets/asset_detail.html:376
#: assets/templates/assets/asset_list.html:38
#: assets/templates/assets/system_user_asset.html:55
#: assets/templates/assets/system_user_list.html:27
msgid "Reachable"
msgstr "可连接"
#: assets/templates/assets/admin_user_assets.html:75
#: assets/templates/assets/cluster_assets.html:68
......@@ -699,7 +808,7 @@ msgstr "任务已下发,查看左侧资产状态"
#: assets/templates/assets/admin_user_create_update.html:16
#: assets/templates/assets/admin_user_list.html:14
#: assets/views/admin_user.py:45
#: assets/views/admin_user.py:48
msgid "Create admin user"
msgstr "创建管理用户"
......@@ -709,7 +818,7 @@ msgstr "使用集群管理用户"
#: assets/templates/assets/admin_user_detail.html:101
#: assets/templates/assets/asset_detail.html:230
#: assets/templates/assets/asset_group_list.html:87
#: assets/templates/assets/asset_group_list.html:85
#: assets/templates/assets/asset_list.html:202
#: assets/templates/assets/cluster_assets.html:104
#: assets/templates/assets/cluster_list.html:89
......@@ -719,25 +828,26 @@ msgstr "使用集群管理用户"
#: users/templates/users/user_detail.html:338
#: users/templates/users/user_detail.html:363
#: users/templates/users/user_detail.html:386
#: users/templates/users/user_group_create_update.html:46
#: users/templates/users/user_group_create_update.html:32
#: users/templates/users/user_group_list.html:82
#: users/templates/users/user_list.html:184
#: users/templates/users/user_profile.html:181
msgid "Confirm"
msgstr "确认"
#: assets/templates/assets/admin_user_list.html:24
#: assets/templates/assets/cluster_list.html:21
#: users/templates/users/_select_user_modal.html:17
msgid "Asset num"
msgstr "资产数量"
#: assets/templates/assets/admin_user_list.html:25
#: assets/templates/assets/admin_user_list.html:26
#: assets/templates/assets/system_user_list.html:28
msgid "Unreachable"
msgstr "不可达"
#: assets/templates/assets/admin_user_list.html:27
#: assets/templates/assets/system_user_list.html:29
#: ops/templates/ops/adhoc_history.html:54
#: ops/templates/ops/task_history.html:57
msgid "Ratio"
msgstr "比例"
#: assets/templates/assets/admin_user_list.html:29
#: assets/templates/assets/asset_group_detail.html:50
#: assets/templates/assets/asset_group_list.html:18
#: assets/templates/assets/asset_list.html:39
......@@ -746,50 +856,22 @@ msgstr "不可达"
#: assets/templates/assets/system_user_list.html:31
#: assets/templates/assets/user_asset_list.html:27
#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:61
#: ops/templates/ops/task_history.html:62 ops/templates/ops/task_list.html:46
#: ops/templates/ops/task_history.html:62 ops/templates/ops/task_list.html:41
#: perms/templates/perms/asset_permission_list.html:32
#: terminal/templates/terminal/session_list.html:78
#: terminal/templates/terminal/session_list.html:79
#: terminal/templates/terminal/terminal_list.html:36
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:28
msgid "Action"
msgstr "动作"
#: assets/templates/assets/admin_user_list.html:47
#: assets/templates/assets/asset_group_detail.html:172
#: assets/templates/assets/asset_group_list.html:42
#: assets/templates/assets/asset_list.html:95
#: assets/templates/assets/cluster_assets.html:170
#: assets/templates/assets/cluster_list.html:43
#: assets/templates/assets/system_user_list.html:84
#: perms/templates/perms/asset_permission_list.html:73
#: terminal/templates/terminal/terminal_list.html:71
#: users/templates/users/user_group_list.html:39
#: users/templates/users/user_list.html:76
msgid "Update"
msgstr "更新"
#: assets/templates/assets/admin_user_list.html:48
#: assets/templates/assets/asset_group_list.html:43
#: assets/templates/assets/asset_list.html:96
#: assets/templates/assets/cluster_list.html:44
#: assets/templates/assets/system_user_list.html:85
#: ops/templates/ops/task_list.html:76
#: perms/templates/perms/asset_permission_list.html:74
#: terminal/templates/terminal/terminal_list.html:73
#: users/templates/users/user_group_list.html:41
#: users/templates/users/user_list.html:80
#: users/templates/users/user_list.html:84
msgid "Delete"
msgstr "删除"
#: assets/templates/assets/asset_create.html:28
#: assets/templates/assets/asset_update.html:33
msgid "Group"
msgstr "组"
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:180
#: assets/views/cluster.py:98
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:186
#: assets/views/cluster.py:103
msgid "Asset detail"
msgstr "资产详情"
......@@ -822,6 +904,7 @@ msgstr "快速修改"
#: assets/templates/assets/asset_list.html:37
#: assets/templates/assets/user_asset_list.html:25 perms/models.py:20
#: perms/templates/perms/asset_permission_create_update.html:47
#: perms/templates/perms/asset_permission_detail.html:116
#: terminal/templates/terminal/terminal_list.html:34
#: users/templates/users/_select_user_modal.html:18
#: users/templates/users/user_detail.html:127
......@@ -847,26 +930,25 @@ msgstr "添加到资产组"
msgid "Update successfully!"
msgstr "更新成功"
#: assets/templates/assets/asset_detail.html:376
#: assets/templates/assets/asset_list.html:38
#: assets/templates/assets/system_user_asset.html:55
#: assets/templates/assets/system_user_list.html:27
msgid "Reachable"
msgstr "可连接"
#: assets/templates/assets/asset_group_detail.html:16
msgid "Group assets"
msgstr "组下资产"
#: assets/templates/assets/asset_group_detail.html:49
#: assets/templates/assets/cluster_assets.html:55
#: terminal/templates/terminal/terminal_list.html:35
msgid "Alive"
msgstr "在线"
#: assets/templates/assets/asset_group_detail.html:62
msgid "Add assets to this group"
msgstr "添加资产到该组"
#: assets/templates/assets/asset_group_detail.html:79
#: perms/templates/perms/asset_permission_asset.html:108
#: perms/templates/perms/asset_permission_asset.html:97
#: perms/templates/perms/asset_permission_detail.html:153
#: perms/templates/perms/asset_permission_user.html:108
#: perms/templates/perms/asset_permission_user.html:136
#: perms/templates/perms/asset_permission_user.html:97
#: perms/templates/perms/asset_permission_user.html:125
#: users/templates/users/user_group_detail.html:95
msgid "Add"
msgstr "添加"
......@@ -875,12 +957,12 @@ msgstr "添加"
msgid "Remove"
msgstr "移除"
#: assets/templates/assets/asset_group_list.html:7 assets/views/group.py:31
#: assets/views/group.py:94
#: assets/templates/assets/asset_group_list.html:7 assets/views/group.py:35
#: assets/views/group.py:88
msgid "Create asset group"
msgstr "创建资产组"
#: assets/templates/assets/asset_group_list.html:82
#: assets/templates/assets/asset_group_list.html:80
#: assets/templates/assets/asset_list.html:197
#: assets/templates/assets/cluster_list.html:84
#: assets/templates/assets/system_user_list.html:129
......@@ -891,29 +973,29 @@ msgstr "创建资产组"
msgid "Are you sure?"
msgstr "你确认吗?"
#: assets/templates/assets/asset_group_list.html:83
#: assets/templates/assets/asset_group_list.html:81
#: users/templates/users/user_group_list.html:78
msgid "This will delete the selected groups !!!"
msgstr "删除选择组"
#: assets/templates/assets/asset_group_list.html:91
#: assets/templates/assets/asset_group_list.html:89
msgid "Group deleted"
msgstr "组已被删除"
#: assets/templates/assets/asset_group_list.html:92
#: assets/templates/assets/asset_group_list.html:97
#: assets/templates/assets/asset_group_list.html:90
#: assets/templates/assets/asset_group_list.html:95
msgid "Group Delete"
msgstr "删除"
#: assets/templates/assets/asset_group_list.html:96
#: assets/templates/assets/asset_group_list.html:94
msgid "Group deleting failed."
msgstr "删除失败"
#: assets/templates/assets/asset_group_list.html:159
#: assets/templates/assets/asset_group_list.html:157
msgid "The selected asset groups has been updated successfully."
msgstr "更新成功"
#: assets/templates/assets/asset_group_list.html:160
#: assets/templates/assets/asset_group_list.html:158
msgid "AssetGroup Updated"
msgstr "资产组更新"
......@@ -1000,7 +1082,7 @@ msgid "Test assets connective"
msgstr "测试资产可连接性"
#: assets/templates/assets/cluster_assets.html:77
#: ops/templates/ops/task_list.html:75
#: ops/templates/ops/task_list.html:70
msgid "Run"
msgstr "执行"
......@@ -1012,14 +1094,20 @@ msgstr "添加资产到"
msgid "Select asset"
msgstr "选择资产"
#: assets/templates/assets/cluster_assets.html:211
#: assets/templates/assets/system_user_asset.html:162
msgid "Task has been send, seen left assets status"
msgstr "任务已下发,查看左侧资产状态"
#: assets/templates/assets/cluster_create_update.html:41
#: users/templates/users/user_profile.html:20
msgid "Settings"
msgstr "设置"
#: assets/templates/assets/cluster_list.html:11 assets/views/cluster.py:39
msgid "Create Cluster"
msgstr "创建Cluster"
#: assets/templates/assets/cluster_list.html:21
#: users/templates/users/_select_user_modal.html:17
msgid "Asset num"
msgstr "资产数量"
#: assets/templates/assets/cluster_list.html:85
msgid "This will delete the selected cluster"
......@@ -1064,10 +1152,6 @@ msgstr "推送"
msgid "Task has been send, Go to ops task list seen result"
msgstr "任务已下发,查看ops任务列表"
#: assets/templates/assets/system_user_asset.html:162
msgid "Task has been send, seen left assets status"
msgstr "任务已下发,查看左侧资产状态"
#: assets/templates/assets/system_user_detail.html:22
msgid "Attached assets"
msgstr "关联的资产"
......@@ -1088,12 +1172,6 @@ msgstr "集群"
msgid "Add to cluster"
msgstr "添加到集群"
#: assets/templates/assets/system_user_list.html:29
#: ops/templates/ops/adhoc_history.html:54
#: ops/templates/ops/task_history.html:57
msgid "Ratio"
msgstr "比例"
#: assets/templates/assets/system_user_list.html:130
msgid "This will delete the selected System Users !!!"
msgstr "删除选择系统用户"
......@@ -1115,84 +1193,93 @@ msgstr "系统用户删除失败"
msgid "Connective"
msgstr "连接性"
#: assets/templates/assets/user_asset_list.html:66
#: assets/templates/assets/user_asset_list.html:65
msgid "Connect"
msgstr "连接"
#: assets/views/admin_user.py:28
#: assets/views/admin_user.py:30
msgid "Admin user list"
msgstr "管理用户列表"
#: assets/views/admin_user.py:52
#, python-brace-format
msgid "Create admin user <a href=\"{url}\">{name}</a> successfully."
msgstr "创建管理用户 <a href=\"{url}\">{name}</a> 成功"
#: assets/views/admin_user.py:68
#: assets/views/admin_user.py:64
msgid "Update admin user"
msgstr "更新管理用户"
#: assets/views/admin_user.py:89 assets/views/admin_user.py:116
#: assets/views/admin_user.py:80 assets/views/admin_user.py:107
msgid "Admin user detail"
msgstr "管理用户详情"
#: assets/views/asset.py:48 assets/views/asset.py:62
#: assets/views/asset.py:49 assets/views/asset.py:62
msgid "Asset list"
msgstr "资产列表"
#: assets/views/asset.py:142
#: assets/views/asset.py:145
msgid "Bulk update asset"
msgstr "批量更新资产"
#: assets/views/asset.py:159
#: assets/views/asset.py:162
msgid "Update asset"
msgstr "编辑资产"
#: assets/views/asset.py:292
#: assets/views/asset.py:298
msgid "already exists"
msgstr "已经存在"
#: assets/views/cluster.py:23
#: assets/views/cluster.py:27
msgid "Cluster list"
msgstr "集群列表"
#: assets/views/cluster.py:38 assets/views/cluster.py:65
#: assets/views/cluster.py:42 assets/views/cluster.py:70
#: assets/views/system_user.py:96
msgid "assets"
msgstr "资产管理"
#: assets/views/cluster.py:66
#: assets/views/cluster.py:43
msgid "Create cluster"
msgstr "创建集群"
#: assets/views/cluster.py:71
msgid "Update Cluster"
msgstr "更新Cluster"
#: assets/views/cluster.py:81
#: assets/views/cluster.py:86
msgid "Cluster detail"
msgstr "集群详情"
#: assets/views/group.py:54
#: assets/views/group.py:53
msgid "Asset group list"
msgstr "资产组列表"
#: assets/views/group.py:72
#: assets/views/group.py:70
msgid "Asset group detail"
msgstr "资产组详情"
#: assets/views/system_user.py:30
#: assets/views/system_user.py:29
msgid "System user list"
msgstr "系统用户列表"
#: assets/views/system_user.py:57
#, python-brace-format
msgid "Create system user <a href=\"{url}\">{name}</a> successfully."
msgstr "创建系统用户 <a href=\"{url}\">{name}</a> 成功"
#: assets/views/system_user.py:72
#: assets/views/system_user.py:61
msgid "Update system user"
msgstr "更新系统用户"
#: assets/views/system_user.py:92
#: assets/views/system_user.py:76
msgid "System user detail"
msgstr "系统用户详情"
#: assets/views/system_user.py:97
msgid "System user asset"
msgstr "系统用户集群资产"
#: common/const.py:6
#, python-format
msgid "<b>%(name)s</b> was created successfully"
msgstr "<b>%(name)s</b> 创建成功"
#: common/const.py:7
#, python-format
msgid "<b>%(name)s</b> was updated successfully"
msgstr "<b>%(name)s</b> 更新成功"
#: common/mixins.py:29
msgid "is discard"
msgstr ""
......@@ -1235,7 +1322,7 @@ msgid "Options"
msgstr "选项"
#: ops/models.py:152 ops/templates/ops/adhoc_detail.html:53
#: ops/templates/ops/task_adhoc.html:56 ops/templates/ops/task_list.html:42
#: ops/templates/ops/task_adhoc.html:56 ops/templates/ops/task_list.html:37
msgid "Hosts"
msgstr "主机"
......@@ -1257,36 +1344,36 @@ msgstr "Become"
msgid "Create by"
msgstr "创建者"
#: ops/models.py:306
#: ops/models.py:307
msgid "Start time"
msgstr "开始时间"
#: ops/models.py:307
#: ops/models.py:308
msgid "End time"
msgstr "完成时间"
#: ops/models.py:308 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:60 ops/templates/ops/task_list.html:45
#: ops/models.py:309 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:60 ops/templates/ops/task_list.html:40
msgid "Time"
msgstr "时间"
#: ops/models.py:309 ops/templates/ops/adhoc_detail.html:106
#: ops/models.py:310 ops/templates/ops/adhoc_detail.html:106
#: ops/templates/ops/adhoc_history.html:55
#: ops/templates/ops/adhoc_history_detail.html:66
#: ops/templates/ops/task_detail.html:80 ops/templates/ops/task_history.html:58
msgid "Is finished"
msgstr "是否完成"
#: ops/models.py:310 ops/templates/ops/adhoc_history.html:56
#: ops/models.py:311 ops/templates/ops/adhoc_history.html:56
#: ops/templates/ops/task_history.html:59
msgid "Is success"
msgstr "是否成功"
#: ops/models.py:311
#: ops/models.py:312
msgid "Adhoc raw result"
msgstr "结果"
#: ops/models.py:312
#: ops/models.py:313
msgid "Adhoc result summary"
msgstr "汇总"
......@@ -1296,7 +1383,7 @@ msgid "Version detail"
msgstr "版本详情"
#: ops/templates/ops/adhoc_detail.html:22
#: ops/templates/ops/adhoc_history.html:22 ops/views.py:120
#: ops/templates/ops/adhoc_history.html:22 ops/views.py:105
msgid "Version run history"
msgstr "执行历史"
......@@ -1308,7 +1395,7 @@ msgstr "执行历史"
msgid "ID"
msgstr "ID"
#: ops/templates/ops/adhoc_detail.html:94 ops/templates/ops/task_list.html:40
#: ops/templates/ops/adhoc_detail.html:94 ops/templates/ops/task_list.html:35
msgid "Run times"
msgstr "执行次数"
......@@ -1352,8 +1439,8 @@ msgstr "执行历史"
#: ops/templates/ops/adhoc_history.html:52
#: ops/templates/ops/adhoc_history_detail.html:58
#: ops/templates/ops/task_history.html:55 terminal/models.py:100
#: terminal/templates/terminal/session_list.html:76
#: ops/templates/ops/task_history.html:55 terminal/models.py:101
#: terminal/templates/terminal/session_list.html:77
msgid "Date start"
msgstr "开始日期"
......@@ -1368,7 +1455,7 @@ msgstr "失败/成功/总"
msgid "Version"
msgstr "版本"
#: ops/templates/ops/adhoc_history_detail.html:19 ops/views.py:133
#: ops/templates/ops/adhoc_history_detail.html:19 ops/views.py:118
msgid "Run history detail"
msgstr "执行历史详情"
......@@ -1394,12 +1481,12 @@ msgid "Success assets"
msgstr "成功资产"
#: ops/templates/ops/task_adhoc.html:19 ops/templates/ops/task_detail.html:19
#: ops/templates/ops/task_history.html:19 ops/views.py:58
#: ops/templates/ops/task_history.html:19 ops/views.py:53
msgid "Task detail"
msgstr "任务详情"
#: ops/templates/ops/task_adhoc.html:22 ops/templates/ops/task_detail.html:22
#: ops/templates/ops/task_history.html:22 ops/views.py:71
#: ops/templates/ops/task_history.html:22 ops/views.py:66
msgid "Task versions"
msgstr "任务各版本"
......@@ -1414,6 +1501,7 @@ msgstr "版本"
#: ops/templates/ops/task_adhoc.html:60
#: terminal/templates/terminal/command_list.html:76
#: terminal/templates/terminal/session_detail.html:50
msgid "Datetime"
msgstr "日期"
......@@ -1429,7 +1517,7 @@ msgstr "最新版本"
msgid "Contents"
msgstr "内容"
#: ops/templates/ops/task_list.html:25 ops/templates/ops/task_list.html:30
#: ops/templates/ops/task_list.html:20 ops/templates/ops/task_list.html:25
#: templates/_base_list.html:43 templates/_header_bar.html:8
#: terminal/templates/terminal/command_list.html:60
#: users/templates/users/login_log_list.html:35
......@@ -1437,73 +1525,74 @@ msgstr "内容"
msgid "Search"
msgstr "搜索"
#: ops/templates/ops/task_list.html:41
#: ops/templates/ops/task_list.html:36
msgid "Versions"
msgstr "版本"
#: ops/templates/ops/task_list.html:43
#: ops/templates/ops/task_list.html:38
msgid "Success"
msgstr "成功"
#: ops/templates/ops/task_list.html:44
#: ops/templates/ops/task_list.html:39
#: users/templates/users/login_log_list.html:54
msgid "Date"
msgstr "日期"
#: ops/views.py:41 ops/views.py:57 ops/views.py:70 ops/views.py:83
#: ops/views.py:106 ops/views.py:119 ops/views.py:132
#: ops/templates/ops/task_list.html:125
msgid "Task start: "
msgstr "任务开始: "
#: ops/views.py:36 ops/views.py:52 ops/views.py:65 ops/views.py:78
#: ops/views.py:91 ops/views.py:104 ops/views.py:117
msgid "Ops"
msgstr "作业中心"
#: ops/views.py:42
#: ops/views.py:37
msgid "Task list"
msgstr "任务列表"
#: ops/views.py:84
#: ops/views.py:79
msgid "Task run history"
msgstr "执行历史"
#: perms/forms.py:16 users/forms.py:144 users/forms.py:149 users/forms.py:161
#: users/forms.py:190
#: users/forms.py:191
msgid "Select users"
msgstr "选择用户"
#: perms/forms.py:18 perms/models.py:15
#: perms/templates/perms/asset_permission_create_update.html:36
#: perms/templates/perms/asset_permission_list.html:26 templates/_nav.html:12
#: templates/_user_profile.html:14 terminal/backends/command/models.py:10
#: terminal/models.py:92 terminal/templates/terminal/command_list.html:32
#: terminal/backends/command/models.py:10 terminal/models.py:92
#: terminal/templates/terminal/command_list.html:32
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/models/user.py:31
#: users/templates/users/user_group_detail.html:78 users/views/user.py:348
#: terminal/templates/terminal/session_list.html:71 users/forms.py:187
#: users/models/user.py:31 users/templates/users/user_group_detail.html:78
#: users/views/user.py:338
msgid "User"
msgstr "用户"
#: perms/forms.py:30 perms/templates/perms/asset_permission_user.html:127
#: perms/forms.py:31 perms/templates/perms/asset_permission_user.html:116
msgid "Select user groups"
msgstr "选择用户组"
#: perms/forms.py:39 perms/templates/perms/asset_permission_detail.html:144
#: users/forms.py:243
msgid "Select system users"
msgstr "选择系统用户"
#: perms/forms.py:44
msgid "User or user group at least one required"
msgstr ""
#: perms/forms.py:52
msgid "User or group at least one required"
msgstr "用户和组至少需要选一个"
#: perms/forms.py:45
msgid "Asset or Asset group at least one required"
msgstr ""
#: perms/forms.py:60
msgid "Asset or group at least one required"
msgstr "资产或组至少需要选择一个"
#: perms/forms.py:59
msgid "Asset {} not have [{}] system users, please check \n"
msgstr ""
#: perms/forms.py:78
msgid "Asset {} of cluster {} not have [{}] system users, please check \n"
msgstr "资产 {} 所在集群 {} 不包含系统用户 [{}] 请检查\n"
#: perms/forms.py:67
msgid "Asset {}: {} not have [{}] system users, please check"
msgstr ""
#: perms/forms.py:87
msgid ""
"Asset {}(group {}) of cluster {} not have [{}] system users, please check \n"
msgstr "资产 {}(组 {}) 所在集群 {} 不包含系统用户 [{}] 请检查\n"
#: perms/models.py:16 perms/templates/perms/asset_permission_list.html:27
#: templates/_nav.html:13 users/models/user.py:38
......@@ -1513,23 +1602,6 @@ msgstr ""
msgid "User group"
msgstr "用户组"
#: perms/models.py:18 perms/templates/perms/asset_permission_list.html:29
#: templates/_nav.html:23
msgid "Asset group"
msgstr "资产组"
#: perms/models.py:19 perms/templates/perms/asset_permission_detail.html:136
#: perms/templates/perms/asset_permission_list.html:30 templates/_nav.html:26
#: terminal/backends/command/models.py:12 terminal/models.py:94
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49
#: terminal/templates/terminal/session_list.html:73
#: users/templates/users/user_granted_asset.html:50
#: users/templates/users/user_group_granted_asset.html:52
msgid "System user"
msgstr "系统用户"
#: perms/models.py:21 perms/templates/perms/asset_permission_detail.html:86
#: users/models/user.py:50 users/templates/users/user_detail.html:94
#: users/templates/users/user_profile.html:96
......@@ -1548,21 +1620,15 @@ msgstr "用户或用户组"
msgid "Assets and asset groups"
msgstr "资产或资产组"
#: perms/templates/perms/asset_permission_asset.html:57
#: perms/templates/perms/asset_permission_list.html:31
#: perms/templates/perms/asset_permission_user.html:57
msgid "Is valid"
msgstr "有效"
#: perms/templates/perms/asset_permission_asset.html:91
#: perms/templates/perms/asset_permission_asset.html:80
msgid "Add asset to this permission"
msgstr "添加资产"
#: perms/templates/perms/asset_permission_asset.html:119
#: perms/templates/perms/asset_permission_asset.html:108
msgid "Add asset group to this permission"
msgstr "添加资产组"
#: perms/templates/perms/asset_permission_asset.html:136
#: perms/templates/perms/asset_permission_asset.html:125
#: users/templates/users/user_detail.html:195
msgid "Join"
msgstr "加入"
......@@ -1596,70 +1662,53 @@ msgstr "系统用户数量"
msgid "Create permission"
msgstr "创建授权规则"
#: perms/templates/perms/asset_permission_list.html:31
msgid "Is valid"
msgstr "有效"
#: perms/templates/perms/asset_permission_user.html:35
msgid "User list of "
msgstr "用户列表"
#: perms/templates/perms/asset_permission_user.html:56 users/models/user.py:37
#: users/templates/users/user_detail.html:70
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
#: perms/templates/perms/asset_permission_user.html:91
#: perms/templates/perms/asset_permission_user.html:80
msgid "Add user to asset permission"
msgstr "添加用户"
#: perms/templates/perms/asset_permission_user.html:99
#: perms/templates/perms/asset_permission_user.html:88
#: users/templates/users/login_log_list.html:28
msgid "Select user"
msgstr "选择用户"
#: perms/templates/perms/asset_permission_user.html:119
#: perms/templates/perms/asset_permission_user.html:108
msgid "Add user group to asset permission"
msgstr "添加用户组"
#: perms/views.py:27 perms/views.py:77 perms/views.py:103 perms/views.py:128
#: perms/views.py:165 perms/views.py:195 templates/_nav.html:30
#: perms/views.py:28 perms/views.py:44 perms/views.py:60 perms/views.py:74
#: perms/views.py:111 perms/views.py:141 templates/_nav.html:30
msgid "Perms"
msgstr "权限管理"
#: perms/views.py:28
#: perms/views.py:29
msgid "Asset permission list"
msgstr "资产授权列表"
#: perms/views.py:63
#, python-brace-format
msgid "Create asset permission <a href=\"{url}\"> {name} </a> successfully."
msgstr "创建授权 <a href=\"{url}\"> {name} </a> 成功"
#: perms/views.py:78
#: perms/views.py:45
msgid "Create asset permission"
msgstr "创建权限规则"
#: perms/views.py:89
#, python-brace-format
msgid "Create asset permission <a href=\"{url}\"> {name} </a> success."
msgstr "创建授权 <a href=\"{url}\"> {name} </a> 成功"
#: perms/views.py:104
#: perms/views.py:61
msgid "Update asset permission"
msgstr "更新资产授权"
#: perms/views.py:115
#, python-brace-format
msgid "Update asset permission <a href=\"{url}\"> {name} </a> success."
msgstr "更新授权 <a href=\"{url}\"> {name} </a> 成功"
#: perms/views.py:129
#: perms/views.py:75
msgid "Asset permission detail"
msgstr "资产授权详情"
#: perms/views.py:166
#: perms/views.py:112
msgid "Asset permission user list"
msgstr "资产授权包含用户"
#: perms/views.py:196
#: perms/views.py:142
msgid "Asset permission asset list"
msgstr "资产组授权包含资产"
......@@ -1671,16 +1720,34 @@ msgstr "欢迎使用Jumpserver开源跳板机系统"
msgid "Help"
msgstr "帮助"
#: templates/_header_bar.html:24 templates/_user_profile.html:29
#: templates/_header_bar.html:33 templates/_nav_user.html:9
#: users/templates/users/_user.html:42
#: users/templates/users/user_password_update.html:37
#: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:57
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:320
msgid "Profile"
msgstr "个人信息"
#: templates/_header_bar.html:37
msgid "Admin page"
msgstr "管理页面"
#: templates/_header_bar.html:39
msgid "User page"
msgstr "用户页面"
#: templates/_header_bar.html:42
msgid "Logout"
msgstr "注销登录"
#: templates/_header_bar.html:28 users/templates/users/login.html:42
#: templates/_header_bar.html:46 users/templates/users/login.html:42
#: users/templates/users/login.html:61
msgid "Login"
msgstr "登录"
#: templates/_header_bar.html:41 templates/_nav.html:4
#: templates/_header_bar.html:59 templates/_nav.html:4
msgid "Dashboard"
msgstr "仪表盘"
......@@ -1714,11 +1781,11 @@ msgstr ""
msgid "Close"
msgstr "关闭"
#: templates/_nav.html:9 users/views/group.py:30 users/views/group.py:46
#: users/views/group.py:72 users/views/group.py:89 users/views/login.py:192
#: users/views/login.py:241 users/views/user.py:55 users/views/user.py:70
#: users/views/user.py:95 users/views/user.py:151 users/views/user.py:308
#: users/views/user.py:322 users/views/user.py:366 users/views/user.py:388
#: templates/_nav.html:9 users/views/group.py:28 users/views/group.py:44
#: users/views/group.py:62 users/views/group.py:79 users/views/login.py:193
#: users/views/login.py:242 users/views/user.py:57 users/views/user.py:72
#: users/views/user.py:92 users/views/user.py:148 users/views/user.py:305
#: users/views/user.py:319 users/views/user.py:356 users/views/user.py:378
msgid "Users"
msgstr "用户管理"
......@@ -1739,11 +1806,11 @@ msgid "Task"
msgstr "任务"
#: templates/_nav.html:47 templates/_nav.html:50
#: terminal/templates/terminal/session_list.html:74
#: terminal/views/command.py:47 terminal/views/session.py:54
#: terminal/views/session.py:77 terminal/views/session.py:94
#: terminal/views/session.py:116 terminal/views/terminal.py:31
#: terminal/views/terminal.py:46 terminal/views/terminal.py:58
#: terminal/templates/terminal/session_list.html:75
#: terminal/views/command.py:47 terminal/views/session.py:75
#: terminal/views/session.py:92 terminal/views/session.py:114
#: terminal/views/terminal.py:31 terminal/views/terminal.py:46
#: terminal/views/terminal.py:58
msgid "Terminal"
msgstr "终端"
......@@ -1755,43 +1822,18 @@ msgstr "在线会话"
msgid "Session offline"
msgstr "离线会话"
#: templates/_nav.html:53 terminal/models.py:98
#: templates/_nav.html:53 terminal/models.py:99
#: terminal/templates/terminal/command_list.html:55
#: terminal/templates/terminal/command_list.html:71
#: terminal/templates/terminal/session_list.html:75
#: terminal/templates/terminal/session_detail.html:48
#: terminal/templates/terminal/session_list.html:76
msgid "Command"
msgstr "命令"
#: templates/_nav.html:73
msgid "Visit us"
msgstr "访问官网"
#: templates/_nav_user.html:4
msgid "My assets"
msgstr "我的资产"
#: templates/_nav_user.html:9 templates/_user_profile.html:19
#: users/templates/users/_user.html:42
#: users/templates/users/user_password_update.html:37
#: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:57
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:323
msgid "Profile"
msgstr "个人信息"
#: templates/_user_profile.html:20
msgid "Profile settings"
msgstr "个人信息设置"
#: templates/_user_profile.html:24
msgid "Admin page"
msgstr "管理页面"
#: templates/_user_profile.html:26
msgid "User page"
msgstr "用户页面"
#: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file"
msgstr "语言播放验证码"
......@@ -1818,16 +1860,12 @@ msgid "Session"
msgstr "会话"
#: terminal/forms.py:15
msgid "A unique addr of every terminal, user browser can arrive it"
msgstr ""
#: terminal/forms.py:16
msgid "Coco ssh listen port"
msgstr ""
msgstr "SSH 监听端口"
#: terminal/forms.py:17
#: terminal/forms.py:16
msgid "Coco http/ws listen port"
msgstr ""
msgstr "Http/Websocket 监听端口"
#: terminal/models.py:15
msgid "Remote Address"
......@@ -1847,7 +1885,7 @@ msgstr "在线会话"
#: terminal/models.py:69
msgid "CPU Usage"
msgstr ""
msgstr "CPU使用"
#: terminal/models.py:70
msgid "Memory Used"
......@@ -1855,34 +1893,39 @@ msgstr "内存使用"
#: terminal/models.py:71
msgid "Connections"
msgstr "连接"
msgstr "连接"
#: terminal/models.py:72
msgid "Threads"
msgstr "线程"
msgstr "线程"
#: terminal/models.py:73
msgid "Boot Time"
msgstr "运行时间"
#: terminal/models.py:97 terminal/templates/terminal/session_list.html:98
#: terminal/models.py:96 terminal/templates/terminal/session_list.html:74
#: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote addr"
msgstr "远端地址"
#: terminal/models.py:98 terminal/templates/terminal/session_list.html:100
msgid "Replay"
msgstr "回放"
#: terminal/models.py:101
#: terminal/models.py:102
msgid "Date end"
msgstr "结束日期"
#: terminal/models.py:118
#: terminal/models.py:119
msgid "Args"
msgstr "参数"
#: terminal/templates/terminal/command_list.html:88
msgid "Goto"
msgstr ""
msgstr "转到"
#: terminal/templates/terminal/session_detail.html:17
#: terminal/views/session.py:117
#: terminal/views/session.py:115
msgid "Session detail"
msgstr "会话详情"
......@@ -1912,19 +1955,19 @@ msgstr "监控"
msgid "Terminate session"
msgstr "终止会话"
#: terminal/templates/terminal/session_list.html:77
#: terminal/templates/terminal/session_list.html:78
msgid "Duration"
msgstr "时长"
#: terminal/templates/terminal/session_list.html:100
#: terminal/templates/terminal/session_list.html:102
msgid "Monitor"
msgstr "监控"
#: terminal/templates/terminal/session_list.html:101
#: terminal/templates/terminal/session_list.html:103
msgid "Terminate"
msgstr "终断"
#: terminal/templates/terminal/session_list.html:117
#: terminal/templates/terminal/session_list.html:119
msgid "Terminate task send, waiting ..."
msgstr "终断任务已发送,请等待"
......@@ -1933,10 +1976,6 @@ msgstr "终断任务已发送,请等待"
msgid "Terminal detail"
msgstr "终端详情"
#: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote address"
msgstr "远端地址"
#: terminal/templates/terminal/terminal_detail.html:51
#: terminal/templates/terminal/terminal_list.html:31
msgid "SSH port"
......@@ -1971,14 +2010,14 @@ msgstr "接受终端注册"
msgid "Info"
msgstr "信息"
#: terminal/views/session.py:55 terminal/views/session.py:95
msgid "Session offline list"
msgstr "离线会话"
#: terminal/views/session.py:78
#: terminal/views/session.py:76
msgid "Session online list"
msgstr "在线会话"
#: terminal/views/session.py:93
msgid "Session offline list"
msgstr "离线会话"
#: terminal/views/terminal.py:32
msgid "Terminal list"
msgstr "终端列表"
......@@ -2119,7 +2158,7 @@ msgstr "Agent"
msgid "Date login"
msgstr "登录日期"
#: users/models/user.py:30 users/models/user.py:255
#: users/models/user.py:30 users/models/user.py:259
msgid "Administrator"
msgstr "管理员"
......@@ -2127,6 +2166,11 @@ msgstr "管理员"
msgid "Application"
msgstr "应用程序"
#: users/models/user.py:37 users/templates/users/user_detail.html:70
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
#: users/models/user.py:39 users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:86
#: users/templates/users/user_list.html:25
......@@ -2146,10 +2190,6 @@ msgstr "微信"
msgid "Enable OTP"
msgstr "二次验证"
#: users/models/user.py:46
msgid "Private key"
msgstr "ssh私钥"
#: users/models/user.py:47 users/templates/users/user_password_update.html:43
#: users/templates/users/user_profile.html:71
#: users/templates/users/user_profile_update.html:43
......@@ -2157,7 +2197,7 @@ msgstr "ssh私钥"
msgid "Public key"
msgstr "ssh公钥"
#: users/models/user.py:258
#: users/models/user.py:262
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
......@@ -2259,7 +2299,7 @@ msgid "Setting"
msgstr "设置"
#: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:16 users/views/user.py:70
#: users/templates/users/user_list.html:16 users/views/user.py:72
msgid "Create user"
msgstr "创建用户"
......@@ -2270,7 +2310,7 @@ msgstr "生成重置密码连接,通过邮件发送给用户"
#: users/templates/users/user_detail.html:19
#: users/templates/users/user_granted_asset.html:18
#: users/templates/users/user_group_granted_asset.html:18
#: users/views/user.py:152
#: users/views/user.py:149
msgid "User detail"
msgstr "用户详情"
......@@ -2306,13 +2346,13 @@ msgstr "已发送邮件到用户邮箱"
msgid ""
"This will reset the user's password. A password-reset email will be sent to "
"the user\\'s mailbox."
msgstr ""
msgstr "重设密码邮件将会发送到用户邮箱"
#: users/templates/users/user_detail.html:348
msgid ""
"The reset-ssh-public-key E-mail has been sent successfully. Please inform "
"the user to update his new ssh public key."
msgstr ""
msgstr "重设秘钥邮件将会发送到用户邮箱"
#: users/templates/users/user_detail.html:349
#: users/templates/users/user_profile.html:144
......@@ -2345,11 +2385,11 @@ msgstr "授权资产"
msgid "Asset groups granted of "
msgstr "授权资产组"
#: users/templates/users/user_group_create_update.html:45
#: users/templates/users/user_group_create_update.html:31
msgid "Cancel"
msgstr "取消"
#: users/templates/users/user_group_detail.html:22 users/views/group.py:90
#: users/templates/users/user_group_detail.html:22 users/views/group.py:80
msgid "User group detail"
msgstr "资产组详情"
......@@ -2361,7 +2401,7 @@ msgstr "添加用户"
msgid "Valid"
msgstr "可用"
#: users/templates/users/user_group_list.html:5 users/views/group.py:47
#: users/templates/users/user_group_list.html:5 users/views/group.py:45
msgid "Create user group"
msgstr "创建用户组"
......@@ -2399,8 +2439,8 @@ msgstr "用户删除失败"
msgid "OTP"
msgstr ""
#: users/templates/users/user_profile.html:100 users/views/user.py:181
#: users/views/user.py:233
#: users/templates/users/user_profile.html:100 users/views/user.py:178
#: users/views/user.py:230
msgid "User groups"
msgstr "用户组"
......@@ -2424,7 +2464,7 @@ msgstr "指纹"
msgid "Update public key"
msgstr "更新密钥"
#: users/templates/users/user_update.html:4 users/views/user.py:95
#: users/templates/users/user_update.html:4 users/views/user.py:92
msgid "Update user"
msgstr "编辑用户"
......@@ -2558,17 +2598,17 @@ msgstr "禁用或失效"
msgid "Password or SSH public key invalid"
msgstr "密码或秘钥不合法"
#: users/views/group.py:31
#: users/views/group.py:29
msgid "User group list"
msgstr "用户组列表"
#: users/views/group.py:73
#: users/views/group.py:63
msgid "Update user group"
msgstr "编辑用户组"
#: users/views/login.py:54
msgid "Please enable cookies and try again."
msgstr ""
msgstr "设置你的浏览器支持cookie"
#: users/views/login.py:83
msgid "Logout success"
......@@ -2578,81 +2618,69 @@ msgstr "退出登录成功"
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
#: users/views/login.py:99
#: users/views/login.py:100
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
#: users/views/login.py:112
#: users/views/login.py:113
msgid "Send reset password message"
msgstr "发送重置密码邮件"
#: users/views/login.py:113
#: users/views/login.py:114
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views/login.py:127
#: users/views/login.py:128
msgid "Reset password success"
msgstr "重置密码成功"
#: users/views/login.py:128
#: users/views/login.py:129
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: users/views/login.py:145 users/views/login.py:158
#: users/views/login.py:146 users/views/login.py:159
msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: users/views/login.py:154
#: users/views/login.py:155
msgid "Password not same"
msgstr "密码不一致"
#: users/views/login.py:192
#: users/views/login.py:193
msgid "First login"
msgstr "首次登陆"
#: users/views/login.py:242
#: users/views/login.py:243
msgid "Login log list"
msgstr "登录日志"
#: users/views/user.py:56
#: users/views/user.py:58
msgid "User list"
msgstr "用户列表"
#: users/views/user.py:66 users/views/user.py:335
#, python-brace-format
msgid "Create user <a href=\"{url}\">{name}</a> successfully."
msgstr "创建用户 <a href=\"{url}\">{name}</a> 成功"
#: users/views/user.py:105
#: users/views/user.py:102
msgid "Bulk update user success"
msgstr "批量更新用户成功"
#: users/views/user.py:210
#: users/views/user.py:207
msgid "Invalid file."
msgstr "文件不合法"
#: users/views/user.py:309
#: users/views/user.py:306
msgid "User granted assets"
msgstr "用户授权资产"
#: users/views/user.py:349
#: users/views/user.py:339
msgid "Profile setting"
msgstr "个人信息设置"
#: users/views/user.py:367
#: users/views/user.py:357
msgid "Password update"
msgstr "密码更新"
#: users/views/user.py:389
#: users/views/user.py:379
msgid "Public key update"
msgstr "秘钥更新"
#~ msgid "Audits"
#~ msgstr "审计中心"
#~ msgid "Proxy log list"
#~ msgstr "Session列表"
#~ msgid "If also set private key, use that first"
#~ msgstr "如果设置私钥,则优先使用密钥"
......@@ -2,11 +2,13 @@
from django.shortcuts import get_object_or_404
from rest_framework import viewsets
from rest_framework import viewsets, generics
from rest_framework.views import Response
from .hands import IsSuperUser
from .models import Task, AdHoc, AdHocRunHistory
from .serializers import TaskSerializer, AdHocSerializer, AdHocRunHistorySerializer
from .tasks import run_ansible_task
class TaskViewSet(viewsets.ModelViewSet):
......@@ -15,6 +17,17 @@ class TaskViewSet(viewsets.ModelViewSet):
permission_classes = (IsSuperUser,)
class TaskRun(generics.RetrieveAPIView):
queryset = Task.objects.all()
serializer_class = TaskViewSet
permission_classes = (IsSuperUser,)
def retrieve(self, request, *args, **kwargs):
task = self.get_object()
run_ansible_task.delay(str(task.id))
return Response({"msg": "start"})
class AdHocViewSet(viewsets.ModelViewSet):
queryset = AdHoc.objects.all()
serializer_class = AdHocSerializer
......
# ~*~ coding: utf-8 ~*~
from users.permissions import IsSuperUser
from users.utils import AdminUserRequiredMixin
\ No newline at end of file
......@@ -235,6 +235,7 @@ class AdHoc(models.Model):
return result.results_raw, result.results_summary
except AnsibleError as e:
logger.error("Failed run adhoc {}, {}".format(self.task.name, e))
pass
@become.setter
def become(self, item):
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......@@ -89,7 +89,7 @@
<td>
<b>
{% for task in object.latest_adhoc.tasks %}
{{ forloop.counter }}. {{ task.name }} : {{ task.action.module }} <br/>
{{ forloop.counter }}. {{ task.name }} ::: {{ task.action.module }} <br/>
{% endfor %}
</b>
</td>
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......
......@@ -67,6 +67,7 @@
{% endif %}
</td>
<td class="text-center">
<a data-uid="{{ object.id }}" class="btn btn-xs btn-primary btn-run">{% trans "Run" %}</a>
<a data-uid="{{ object.id }}" class="btn btn-xs btn-danger btn-del">{% trans "Delete" %}</a>
</td>
</tr>
......@@ -98,10 +99,32 @@ $(document).ready(function() {
}).on('click', '.btn-del', function () {
var $this = $(this);
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html();
var name = $this.closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-ops:task-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url);
}).on('click', '.btn-run', function () {
var $this = $(this);
var name = $this.closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-ops:task-run" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var error = function (data) {
alert(data)
};
var success = function () {
setTimeout(function () {
console.log("ok")
}, 1000);
window.location = "{% url 'ops:task-detail' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', uid);
};
APIUpdateAttr({
url: the_url,
error: error,
method: 'GET',
success: success,
success_message: "{% trans 'Task start: ' %}" + " " + name
});
})
</script>
{% endblock %}
......
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from django.conf.urls import url
from rest_framework.routers import DefaultRouter
from .. import api
......@@ -12,6 +13,8 @@ router.register(r'v1/tasks', api.TaskViewSet, 'task')
router.register(r'v1/adhoc', api.AdHocViewSet, 'adhoc')
router.register(r'v1/history', api.AdHocRunHistorySet, 'history')
urlpatterns = []
urlpatterns = [
url(r'^v1/tasks/(?P<pk>[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'),
]
urlpatterns += router.urls
......@@ -6,9 +6,10 @@ from django.views.generic import ListView, DetailView
from common.mixins import DatetimeSearchMixin
from .models import Task, AdHoc, AdHocRunHistory
from .hands import AdminUserRequiredMixin
class TaskListView(DatetimeSearchMixin, ListView):
class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Task
ordering = ('-date_created',)
......@@ -42,7 +43,7 @@ class TaskListView(DatetimeSearchMixin, ListView):
return super().get_context_data(**kwargs)
class TaskDetailView(DetailView):
class TaskDetailView(AdminUserRequiredMixin, DetailView):
model = Task
template_name = 'ops/task_detail.html'
......@@ -55,7 +56,7 @@ class TaskDetailView(DetailView):
return super().get_context_data(**kwargs)
class TaskAdhocView(DetailView):
class TaskAdhocView(AdminUserRequiredMixin, DetailView):
model = Task
template_name = 'ops/task_adhoc.html'
......@@ -68,7 +69,7 @@ class TaskAdhocView(DetailView):
return super().get_context_data(**kwargs)
class TaskHistoryView(DetailView):
class TaskHistoryView(AdminUserRequiredMixin, DetailView):
model = Task
template_name = 'ops/task_history.html'
......@@ -81,7 +82,7 @@ class TaskHistoryView(DetailView):
return super().get_context_data(**kwargs)
class AdHocDetailView(DetailView):
class AdHocDetailView(AdminUserRequiredMixin, DetailView):
model = AdHoc
template_name = 'ops/adhoc_detail.html'
......@@ -94,7 +95,7 @@ class AdHocDetailView(DetailView):
return super().get_context_data(**kwargs)
class AdHocHistoryView(DetailView):
class AdHocHistoryView(AdminUserRequiredMixin, DetailView):
model = AdHoc
template_name = 'ops/adhoc_history.html'
......@@ -107,7 +108,7 @@ class AdHocHistoryView(DetailView):
return super().get_context_data(**kwargs)
class AdHocHistoryDetailView(DetailView):
class AdHocHistoryDetailView(AdminUserRequiredMixin, DetailView):
model = AdHocRunHistory
template_name = 'ops/adhoc_history_detail.html'
......
......@@ -15,7 +15,8 @@ class AssetPermissionForm(forms.ModelForm):
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select users')},
),
label=_("User")
label=_("User"),
required=False,
)
class Meta:
......@@ -41,35 +42,54 @@ class AssetPermissionForm(forms.ModelForm):
help_texts = {
'name': '* required',
'system_users': '* required',
'user_groups': _('User or user group at least one required'),
'asset_groups': _('Asset or Asset group at least one required'),
}
def clean_user_groups(self):
users = self.cleaned_data.get('users')
user_groups = self.cleaned_data.get('user_groups')
if not users and not user_groups:
raise forms.ValidationError(_("User or group at least one required"))
return self.cleaned_data["user_groups"]
def clean_asset_groups(self):
assets = self.cleaned_data.get('assets')
asset_groups = self.cleaned_data.get('asset_groups')
if not assets and not asset_groups:
raise forms.ValidationError(_("Asset or group at least one required"))
return self.cleaned_data["asset_groups"]
def clean_system_users(self):
from assets.utils import check_assets_have_system_user
errors = []
assets = self.cleaned_data['assets']
asset_groups = self.cleaned_data['asset_groups']
system_users = self.cleaned_data['system_users']
asset_groups = self.cleaned_data.get('asset_groups')
system_users = self.cleaned_data.get('system_users')
if not asset_groups and not assets:
return self.cleaned_data.get("system_users")
error_data = check_assets_have_system_user(assets, system_users)
if error_data:
for asset, system_users in error_data.items():
msg = _("Asset {} not have [{}] system users, please check \n")
msg = _("Asset {} of cluster {} not have [{}] system users, please check \n")
error = forms.ValidationError(msg.format(
asset.hostname,
asset.cluster.name,
", ".join(system_user.name for system_user in system_users)
))
errors.append(error)
for group in asset_groups:
msg = _("Asset {}: {} not have [{}] system users, please check")
msg = _("Asset {}(group {}) of cluster {} not have [{}] system users, please check \n")
assets = group.assets.all()
error_data = check_assets_have_system_user(assets, system_users)
for asset, system_users in error_data.items():
errors.append(msg.format(
group.name, asset.hostname,
asset.hostname, group.name, asset.cluster.name,
", ".join(system_user.name for system_user in system_users)
))
if errors:
......
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -53,8 +53,6 @@
<tr>
<th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Is valid' %}</th>
<th></th>
</tr>
</thead>
......@@ -63,15 +61,6 @@
<tr>
<td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td>
<td>{{ user.port }}</td>
<td>
{% if asset.is_active %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td>
<button title="{{ asset.inherit_from_asset_groups }}" data-gid="{{ asset.id }}" class="btn btn-danger btn-xs btn-remove-asset {% if asset.is_inherit_from_asset_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
......
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
......@@ -27,11 +27,11 @@
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-perm">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
......@@ -113,7 +113,7 @@
<table class="table">
<tbody>
<tr class="no-borders-tr">
<td width="50%">Active:</td>
<td width="50%">{% trans 'Active' %} :</td>
<td><span style="float: right">
<div class="switch">
<div class="onoffswitch">
......@@ -139,8 +139,8 @@
<table class="table" id="system-user-table">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<tr class="no-borders-tr">
<td colspan="2">
<select data-placeholder="{% trans 'Select system users' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for system_user in system_users_remain %}
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}</option>
......@@ -148,15 +148,15 @@
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<tr class="no-borders-tr">
<td colspan="2">
<button type="button" class="btn btn-info btn-small" id="btn-add-system-user">{% trans 'Add' %}</button>
</td>
</tr>
</form>
{% for system_user in system_users %}
<tr>
<tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %} >
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user.name }}</b></td>
<td>
<button class="btn btn-danger btn-xs btn-remove-user" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
......@@ -237,6 +237,16 @@ $(document).ready(function () {
}).get();
updateSystemUser(system_users);
$tr.remove()
}).on('click', '#is_active', function () {
var the_url = '{% url "api-perms:asset-permission-detail" pk=asset_permission.id %}';
var checked = $(this).prop('checked');
var body = {
'is_active': checked
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
});
})
</script>
{% endblock %}
......@@ -69,9 +69,11 @@ function initTable() {
$(td).html('<i class="fa fa-check text-navy"></i>')
}
}},
{targets: 8, createdCell: function (td, cellData) {
{targets: 8, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "perms:asset-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del-permission" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-uid="{{ DEFAULT_PK }}" data-name="99991938">{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', rowData.name);
$(td).html(update_btn + del_btn);
}}
......
......@@ -3,8 +3,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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......@@ -53,8 +53,6 @@
<tr>
<th>{% trans 'Name' %}</th>
<th>{% trans 'Username' %}</th>
<th>{% trans 'Email' %}</th>
<th>{% trans 'Is valid' %}</th>
<th></th>
</tr>
</thead>
......@@ -63,15 +61,6 @@
<tr>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>
{% if user.is_expired and user.is_active %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td>
<button class="btn btn-danger btn-xs btn-remove-user {% if user.is_inherit_from_user_groups %} disabled {% endif %}" data-gid="{{ user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td>
......@@ -231,7 +220,6 @@ $(document).ready(function () {
$.map(jumpserver.users_selected, function(value, index) {
users_id.push(index);
});
console.log(users_id);
addUsers(users_id);
}).on('click', '.btn-remove-user', function () {
var user_id = $(this).data("gid");
......
......@@ -7,9 +7,7 @@ from .. import api
app_name = 'perms'
router = routers.DefaultRouter()
router.register('v1/asset-permissions',
api.AssetPermissionViewSet,
'asset-permission')
router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
urlpatterns = [
# 用户可以使用自己的Token或其它认证查看自己授权的资产,资产组等
......
......@@ -11,6 +11,7 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.contrib import messages
from common.const import create_success_msg, update_success_msg
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \
Asset, AssetGroup
from .models import AssetPermission
......@@ -31,46 +32,12 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView):
return super().get_context_data(**kwargs)
class MessageMixin:
def form_valid(self, form):
response = super().form_valid(form)
errors = self.object.check_system_user_in_assets()
if errors:
message = self.get_warning_messages(errors)
messages.warning(self.request, message)
else:
message = self.get_success_message(form.cleaned_data)
messages.success(self.request, message)
success_message = self.get_success_message(form.cleaned_data)
if success_message:
messages.success(self.request, success_message)
return response
@staticmethod
def get_warning_messages(errors):
message = "<b><i class='fa fa-warning'></i>WARNING: System user " \
"should in behind clusters, so that " \
"system user cat auto push to the cluster assets:</b> <br>"
for system_user, clusters in errors.items():
message += " >>> {}: {} ".format(system_user.name, ", ".join((cluster.name for cluster in clusters)))
return message
def get_success_message(self, cleaned_data):
url = reverse_lazy('perms:asset-permission-detail',
kwargs={'pk': self.object.pk})
success_message = _(
'Create asset permission <a href="{url}"> {name} </a> '
'successfully.'.format(url=url, name=self.object.name))
return success_message
class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy('perms:asset-permission-list')
warning = None
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -80,23 +47,13 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, Cre
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'perms:asset-permission-detail',
kwargs={'pk': self.object.pk}
)
success_message = _(
'Create asset permission <a href="{url}"> {name} </a> '
'success.'.format(url=url, name=self.object.name)
)
return success_message
class AssetPermissionUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy("perms:asset-permission-list")
success_message = update_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -106,17 +63,6 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, Upd
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'perms:asset-permission-detail',
kwargs={'pk': self.object.pk}
)
success_message = _(
'Update asset permission <a href="{url}"> {name} </a> '
'success.'.format(url=url, name=self.object.name)
)
return success_message
class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'perms/asset_permission_detail.html'
......
......@@ -61,7 +61,6 @@ function GetTableDataBox() {
id_list.push(i);
}
}
console.log(id_list);
for (i in id_list) {
console.log(tabProduct);
tableData.push(GetRowData(tabProduct.rows[id_list[i]]));
......@@ -357,5 +356,15 @@ String.prototype.format = function(args) {
function setCookie(key, value) {
var expires = new Date();
expires.setTime(expires.getTime() + (24 * 60 * 60 * 1000));
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
}
function delCookie(key) {
var expires = new Date();
expires.setTime(expires.getTime() - 1);
var val = getCookie(key);
if (val !== null) {
document.cookie = key + '=' + val + ";expires" + expires.toUTCString() + ';path=/';
}
}
......@@ -3,8 +3,8 @@
{% load static %}
{% load bootstrap3 %}
{% 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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% block custom_head_css_js_create %} {% endblock %}
{% endblock %}
......
......@@ -3,8 +3,8 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/datatables/datatables.min.css" %}" rel="stylesheet">
<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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/datatables/datatables.min.js" %}"></script>
{% endblock %}
{% block content %}
......
......@@ -3,11 +3,11 @@
<nav class="navbar navbar-static-top white-bg" role="navigation" style="margin-bottom: 0">
<div class="navbar-header">
<a class="navbar-minimalize minimalize-styl-2 btn btn-primary " href="#"><i class="fa fa-bars"></i> </a>
<form role="search" class="navbar-form-custom" method="get" action="">
<div class="form-group">
<input type="text" placeholder="{% trans 'Search' %}..." class="form-control" name="search" id="top-search">
</div>
</form>
<!--<form role="search" class="navbar-form-custom" method="get" action="">-->
<!--<div class="form-group">-->
<!--<input type="text" placeholder="{% trans 'Search' %}..." class="form-control" name="search" id="top-search">-->
<!--</div>-->
<!--</form>-->
</div>
<ul class="nav navbar-top-links navbar-right">
<li>
......@@ -19,7 +19,7 @@
</a>
</li>
<li class="dropdown">
{% if user.is_authenticated %}
{% if request.user.is_authenticated %}
<a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="m-r-sm text-muted welcome-message">
<img alt="image" class="img-circle" width="40" height="40" src="{{ request.user.avatar_url }}"/>
......@@ -30,17 +30,16 @@
</span>
</a>
<ul class="dropdown-menu animated fadeInRight m-t-xs">
<li><a href="{% url 'users:user-profile' %}">{% trans 'Profile' %}</a></li>
<li><a href="{% url 'users:user-profile-update' %}">{% trans 'Profile settings' %}</a></li>
<li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
<li class="divider"></li>
{% if request.user.is_superuser %}
{% if request.COOKIES.IN_ADMIN_PAGE == 'No' %}
<li><a id="switch_admin">{% trans 'Admin page' %}</a></li>
{% else %}
<li><a id="switch_user">{% trans 'User page' %}</a></li>
{% if request.COOKIES.IN_ADMIN_PAGE == 'No' %}
<li><a id="switch_admin"><i class="fa fa-exchange"></i><span> {% trans 'Admin page' %}</span></a></li>
{% else %}
<li><a id="switch_user"><i class="fa fa-exchange"></i><span> {% trans 'User page' %}</span></a></li>
{% endif %}
{% endif %}
{% endif %}
<li><a href="{% url 'users:logout' %}">{% trans 'Logout' %}</a></li>
<li><a href="{% url 'users:logout' %}"><i class="fa fa-sign-out"></i> {% trans 'Logout' %}</a></li>
</ul>
{% else %}
<a href="{% url 'users:login' %}">
......
......@@ -18,16 +18,19 @@
$(document).ready(function () {
})
.on('click', '#switch_admin', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () {
setCookie("IN_ADMIN_PAGE", "Yes");
delCookie(cookieName);
setCookie(cookieName, "Yes");
window.location = "/"
}, 100)
})
.on('click', '#switch_user', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () {
console.log("Set to No");
setCookie("IN_ADMIN_PAGE", "No");
window.location = "/"
delCookie(cookieName);
setCookie(cookieName, "No");
window.location = "{% url 'assets:user-asset-list' %}"
}, 100);
})
</script>
......@@ -110,7 +110,6 @@ class StatusViewSet(viewsets.ModelViewSet):
def handle_sessions(self):
sessions_active = []
for session_data in self.request.data.get("sessions", []):
self.create_or_update_session(session_data)
if not session_data["is_finished"]:
......@@ -165,7 +164,7 @@ class StatusViewSet(viewsets.ModelViewSet):
class SessionViewSet(viewsets.ModelViewSet):
queryset = Session.objects.all()
serializers_class = SessionSerializer
serializer_class = SessionSerializer
permission_classes = (IsSuperUserOrAppUser,)
def get_queryset(self):
......
......@@ -12,7 +12,6 @@ class TerminalForm(forms.ModelForm):
model = Terminal
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment']
help_texts = {
'remote_addr': _('A unique addr of every terminal, user browser can arrive it'),
'ssh_port': _("Coco ssh listen port"),
'http_port': _("Coco http/ws listen port"),
}
......
......@@ -93,6 +93,7 @@ class Session(models.Model):
asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
system_user = models.CharField(max_length=128, verbose_name=_("System user"))
login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST")
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
is_finished = models.BooleanField(default=False)
has_replay = models.BooleanField(default=False, verbose_name=_("Replay"))
has_command = models.BooleanField(default=False, verbose_name=_("Command"))
......
......@@ -45,9 +45,9 @@
<thead>
<tr>
<th data-toggle="true">ID</th>
<th>Command</th>
<th>{% trans 'Command' %}</th>
<th data-hide="all"></th>
<th>Datetime</th>
<th>{% trans 'Datetime' %}</th>
</tr>
</thead>
<tbody>
......@@ -84,24 +84,24 @@
<table class="table">
<tbody>
{% if object.is_finished %}
<tr>
<td class="no-borders">{% trans 'Replay session' %}:</td>
<td class="no-borders">
<tr class="no-borders-tr">
<td>{% trans 'Replay session' %}:</td>
<td>
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Go' %}</button>
<button type="button" onclick="window.open('/luna/replay/{{ object.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Go' %}</button>
</span>
</td>
</tr>
{% else %}
<tr>
<td class="no-borders" >{% trans 'Monitor session' %}:</td>
<td class="no-borders" >
<span class="pull-right">
<button type="button" class="btn btn-primary btn-xs " style="width: 54px">{% trans 'Go' %}</button>
</span>
</td>
</tr>
<tr>
<!--<tr>-->
<!--<td class="no-borders" >{% trans 'Monitor session' %}:</td>-->
<!--<td class="no-borders" >-->
<!--<span class="pull-right">-->
<!--<button type="button" class="btn btn-primary btn-xs " style="width: 54px">{% trans 'Go' %}</button>-->
<!--</span>-->
<!--</td>-->
<!--</tr>-->
<tr class="no-borders-tr">
<td>{% trans 'Terminate session' %}:</td>
<td>
<span class="pull-right">
......
......@@ -71,6 +71,7 @@
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Remote addr' %}</th>
<th class="text-center">{% trans 'Terminal' %}</th>
<th class="text-center">{% trans 'Command' %}</th>
<th class="text-center">{% trans 'Date start' %}</th>
......@@ -88,6 +89,7 @@
<td class="text-center">{{ session.user }}</td>
<td class="text-center">{{ session.asset }}</td>
<td class="text-center">{{ session.system_user }}</td>
<td class="text-center">{{ session.remote_addr|default:"" }}</td>
<td class="text-center">{{ session.terminal.name }}</td>
<td class="text-center">{{ session.id | get_session_command_amount }}</td>
......@@ -97,7 +99,7 @@
{% if session.is_finished %}
<a onclick="window.open('/luna/replay/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>
{% else %}
<a onclick="window.open('/luna/monitor/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-monitor" >{% trans "Monitor" %}</a>
<!--<a onclick="window.open('/luna/monitor/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-monitor" >{% trans "Monitor" %}</a>-->
<a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
{% endif %}
</td>
......
......@@ -13,7 +13,7 @@
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Terminal detail' %} </a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'terminal:terminal-update' pk=terminal.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'terminal:terminal-update' pk=terminal.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
</ul>
</div>
......@@ -44,7 +44,7 @@
<td><b>{{ terminal.name }}</b></td>
</tr>
<tr>
<td>{% trans 'Remote address' %}:</td>
<td>{% trans 'Remote addr' %}:</td>
<td><b>{{ terminal.remote_addr }}</b></td>
</tr>
<tr>
......
......@@ -136,7 +136,6 @@ $(document).ready(function(){
}).on('click', '.btn-connect', function () {
var $this = $(this);
var id = $this.data('id');
console.log(id)
})
</script>
{% endblock %}
......@@ -3,8 +3,8 @@
{% load static %}
{% load bootstrap3 %}
{% 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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %}
......
# -*- coding: utf-8 -*-
#
from django.views.generic import ListView, UpdateView, DeleteView, DetailView, TemplateView
from django.views.generic import ListView
from django.views.generic.edit import SingleObjectMixin
from django.utils.translation import ugettext as _
from django.utils import timezone
......@@ -51,9 +51,9 @@ class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
def get_context_data(self, **kwargs):
context = {
'user_list': utils.get_user_list_from_cache(),
'asset_list': utils.get_asset_list_from_cache(),
'system_user_list': utils.get_system_user_list_from_cache(),
'user_list': utils.get_session_user_list(),
'asset_list': utils.get_session_asset_list(),
'system_user_list': utils.get_session_system_user_list(),
'date_from': self.date_from,
'date_to': self.date_to,
'user': self.user,
......
......@@ -128,7 +128,7 @@ class UserAuthApi(APIView):
user_agent = request.data.get('HTTP_USER_AGENT', '')
if not login_ip:
login_ip = request.META.get("REMOTE_ADDR")
login_ip = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get("REMOTE_ADDR")
user, msg = check_user_valid(
username=username, password=password,
......
......@@ -29,7 +29,7 @@ class UserCreateUpdateForm(forms.ModelForm):
model = User
fields = [
'username', 'name', 'email', 'groups', 'wechat',
'phone', 'role', 'date_expired', 'comment', 'password'
'phone', 'role', 'date_expired', 'comment',
]
help_texts = {
'username': '* required',
......@@ -38,13 +38,16 @@ class UserCreateUpdateForm(forms.ModelForm):
}
widgets = {
'groups': forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Join user groups')}),
attrs={
'class': 'select2',
'data-placeholder': _('Join user groups')
}
),
}
def save(self, commit=True):
user = super().save(commit=commit)
password = self.cleaned_data.get('password')
user = super().save(commit=commit)
if password:
user.set_password(password)
user.save()
......@@ -184,12 +187,14 @@ class UserBulkUpdateForm(forms.ModelForm):
class UserGroupForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
label=_("User"),
widget=forms.SelectMultiple(
attrs={
'class': 'select2',
'data-placeholder': _('Select users')
}
)
),
required=False,
)
def __init__(self, **kwargs):
......
......@@ -168,6 +168,11 @@ class User(AbstractUser):
token = PrivateToken.objects.create(user=self)
return token.key
def create_access_key(self):
from . import AccessKey
access_key = AccessKey.objects.create(user=self)
return access_key
def refresh_private_token(self):
from .authentication import PrivateToken
PrivateToken.objects.filter(user=self).delete()
......@@ -214,13 +219,12 @@ class User(AbstractUser):
@classmethod
def create_app_user(cls, name, comment):
from . import AccessKey
app = cls.objects.create(
username=name, name=name, email='{}@local.domain'.format(name),
is_active=False, role='App', enable_otp=False, comment=comment,
is_first_login=False, created_by='System'
)
access_key = AccessKey.objects.create(user=app)
access_key = app.create_access_key()
return app, access_key
@classmethod
......
......@@ -3,9 +3,9 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %}
{% block content %}
......@@ -22,11 +22,11 @@
<a href="{% url 'users:user-granted-asset' pk=user_object.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-update' pk=user_object.id %}"><i class="fa fa-edit"></i>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
......
......@@ -3,8 +3,8 @@
{% load i18n %}
{% load bootstrap3 %}
{% 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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
......@@ -25,20 +25,6 @@
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.users layout="horizontal" %}
{# <div class="form-group">#}
{# <label for="users" class="col-sm-2 control-label">{% trans 'Users' %}</label>#}
{# <div class="col-sm-9">#}
{# <select name="users" id="id_users" data-placeholder="{% trans 'Select User' %}" class="select2 form-control m-b" multiple tabindex="2">#}
{# {% for user in users %}#}
{# {% if user.id in group_users %}#}
{# <option value="{{ user.id }}" selected>{{ user.name }}</option>#}
{# {% else %}#}
{# <option value="{{ user.id }}">{{ user.name }}</option>#}
{# {% endif %}#}
{# {% endfor %}#}
{# </select>#}
{# </div>#}
{# </div>#}
{% bootstrap_field form.comment layout="horizontal" %}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
......@@ -57,7 +43,9 @@
{% block custom_foot_js %}
<script>
$(document).ready(function () {
$('.select2').select2();
$('.select2').select2({
closeOnSelect: false
});
})
</script>
{% endblock %}
......@@ -3,11 +3,11 @@
{% load i18n %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/datatables/datatables.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
<script src="{% static "js/plugins/datatables/datatables.min.js" %}"></script>
{% endblock %}
......@@ -25,11 +25,11 @@
{# <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>Update</a>
<a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user-group">
<i class="fa fa-edit"></i>Delete
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
</ul>
......@@ -171,7 +171,6 @@ $(document).ready(function () {
var users = $('.bdg_user').map(function() {
return $(this).data('uid');
}).get();
console.log(users);
updateGroupMember(users)
}).on('click', '#btn_add_user', function() {
if (Object.keys(jumpserver.users_selected).length === 0) {
......
......@@ -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/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
......
......@@ -223,7 +223,7 @@ $(document).ready(function(){
var $this = $(this);
var name = $this.data('name');
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk='00000000-0000-0000-0000-000000000000' %}'.replace('00000000-0000-0000-0000-000000000000', uid);
var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", uid);
objectDelete($this, name, the_url);
})
</script>
......
......@@ -2,17 +2,15 @@
from __future__ import unicode_literals
from django import forms
from django.shortcuts import reverse, redirect
from django.utils.translation import ugettext as _
from django.urls import reverse_lazy
from django.views.generic import ListView
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, FormMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.detail import DetailView
from django.contrib.messages.views import SuccessMessageMixin
from common.utils import get_logger
from perms.models import AssetPermission
from common.const import create_success_msg, update_success_msg
from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin
from .. import forms
......@@ -39,7 +37,7 @@ class UserGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
success_message = '<a href={url}> {name} </a> was created successfully'
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
......@@ -49,21 +47,13 @@ class UserGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'users:user-group-detail',
kwargs={'pk': self.object.id}
)
return self.success_message.format(
url=url, name=self.object.name
)
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
class UserGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = UserGroup
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
success_message = update_success_msg
def get_context_data(self, **kwargs):
users = User.objects.all()
......
......@@ -53,7 +53,8 @@ class UserLoginView(FormView):
if not self.request.session.test_cookie_worked():
return HttpResponse(_("Please enable cookies and try again."))
auth_login(self.request, form.get_user())
login_ip = self.request.META.get('REMOTE_ADDR', '')
login_ip = self.request.META.get('HTTP_X_FORWARDED_FOR') or \
self.request.META.get('REMOTE_ADDR', '')
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
write_login_log_async.delay(
self.request.user.username, type='W',
......@@ -82,6 +83,7 @@ class UserLogoutView(TemplateView):
context = {
'title': _('Logout success'),
'messages': _('Logout success, return login page'),
'interval': 1,
'redirect_url': reverse('users:login'),
'auto_redirect': True,
}
......
......@@ -27,12 +27,14 @@ from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import logout as auth_logout
from common.const import create_success_msg, update_success_msg
from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid
from .. import forms
from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin
from ..signals import on_user_created
from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid
__all__ = [
'UserListView', 'UserCreateView', 'UserDetailView',
......@@ -63,7 +65,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_create.html'
success_url = reverse_lazy('users:user-list')
success_message = _('Create user <a href="{url}">{name}</a> successfully.')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
......@@ -77,19 +79,14 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
on_user_created.send(self.__class__, user=user)
return super().form_valid(form)
def get_success_message(self, cleaned_data):
url = reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk})
return self.success_message.format(
url=url, name=self.object.name
)
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = User
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_update.html'
context_object_name = 'user_object'
success_url = reverse_lazy('users:user-list')
success_message = update_success_msg
def get_context_data(self, **kwargs):
context = {'app': _('Users'), 'action': _('Update user')}
......@@ -332,17 +329,10 @@ class UserProfileUpdateView(LoginRequiredMixin, UpdateView):
model = User
form_class = forms.UserProfileForm
success_url = reverse_lazy('users:user-profile')
success_message = _('Create user <a href="{url}">{name}</a> successfully.')
def get_object(self, queryset=None):
return self.request.user
def get_success_message(self, cleaned_data):
url = reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk})
return self.success_message.format(
url=url, name=self.object.name
)
def get_context_data(self, **kwargs):
context = {
'app': _('User'),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment