[Feature] 资产节点增加批量测试可连接性和更新硬件信息

pull/1068/head
ibuler 2018-03-12 11:41:12 +08:00
parent f59f03adfd
commit 43b4b7c55e
7 changed files with 157 additions and 62 deletions

View File

@ -18,10 +18,12 @@ from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.shortcuts import get_object_or_404
from common.utils import get_logger, get_object_or_none from common.utils import get_logger, get_object_or_none
from ..hands import IsSuperUser from ..hands import IsSuperUser
from ..models import Node from ..models import Node
from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util
from .. import serializers from .. import serializers
@ -29,7 +31,8 @@ logger = get_logger(__file__)
__all__ = [ __all__ = [
'NodeViewSet', 'NodeChildrenApi', 'NodeViewSet', 'NodeChildrenApi',
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeAddAssetsApi', 'NodeRemoveAssetsApi',
'NodeAddChildrenApi', 'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
'TestNodeConnectiveApi'
] ]
@ -117,3 +120,30 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
instance = self.get_object() instance = self.get_object()
if instance != Node.root(): if instance != Node.root():
instance.assets.remove(*tuple(assets)) instance.assets.remove(*tuple(assets))
class RefreshNodeHardwareInfoApi(APIView):
permission_classes = (IsSuperUser,)
model = Node
def get(self, request, *args, **kwargs):
node_id = kwargs.get('pk')
node = get_object_or_404(self.model, id=node_id)
assets = node.assets.all()
task_name = _("Refresh node assets hardware info: {}".format(node.name))
update_assets_hardware_info_util.delay(assets, task_name=task_name)
return Response({"msg": "Task created"})
class TestNodeConnectiveApi(APIView):
permission_classes = (IsSuperUser,)
model = Node
def get(self, request, *args, **kwargs):
node_id = kwargs.get('pk')
node = get_object_or_404(self.model, id=node_id)
assets = node.assets.all()
task_name = _("Test node assets connective: {}".format(node.name))
test_asset_connectability_util.delay(assets, task_name=task_name)
return Response({"msg": "Task created"})

View File

@ -21,7 +21,7 @@ def update_asset_hardware_info_on_created(asset):
def test_asset_conn_on_created(asset): def test_asset_conn_on_created(asset):
logger.debug("Test asset `{}` connectability".format(asset)) logger.debug("Test asset `{}` connectability".format(asset))
test_asset_connectability_util.delay(asset) test_asset_connectability_util.delay([asset])
def set_asset_root_node(asset): def set_asset_root_node(asset):

View File

@ -213,12 +213,12 @@ def test_admin_user_connectability_manual(admin_user):
@shared_task @shared_task
def test_asset_connectability_util(asset, task_name=None): def test_asset_connectability_util(assets, task_name=None):
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
if task_name is None: if task_name is None:
task_name = _("Test asset connectability") task_name = _("Test assets connectability")
hosts = [asset.hostname] hosts = [asset.hostname for asset in assets]
if not hosts: if not hosts:
logger.info("No hosts, passed") logger.info("No hosts, passed")
return {} return {}
@ -229,18 +229,17 @@ def test_asset_connectability_util(asset, task_name=None):
) )
result = task.run() result = task.run()
summary = result[1] summary = result[1]
if summary.get('dark'): for k in summary.get('dark'):
cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(asset.hostname), 0, cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(k), 0, CACHE_MAX_TIME)
CACHE_MAX_TIME)
else: for k in summary.get('contacted'):
cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(asset.hostname), 1, cache.set(const.ASSET_ADMIN_CONN_CACHE_KEY.format(k), 1, CACHE_MAX_TIME)
CACHE_MAX_TIME)
return summary return summary
@shared_task @shared_task
def test_asset_connectability_manual(asset): def test_asset_connectability_manual(asset):
summary = test_asset_connectability_util(asset) summary = test_asset_connectability_util([asset])
if summary.get('dark'): if summary.get('dark'):
return False, summary['dark'] return False, summary['dark']

View File

@ -119,6 +119,8 @@
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li id="menu_asset_create" class="btn-create-asset" tabindex="-1"><a>{% trans 'Create asset' %}</a></li> <li id="menu_asset_create" class="btn-create-asset" tabindex="-1"><a>{% trans 'Create asset' %}</a></li>
<li id="menu_asset_add" class="btn-add-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a>{% trans 'Add asset' %}</a></li> <li id="menu_asset_add" class="btn-add-asset" data-toggle="modal" data-target="#asset_list_modal" tabindex="0"><a>{% trans 'Add asset' %}</a></li>
<li id="menu_refresh_hardware_info" class="btn-refresh-hardware" tabindex="-1"><a>{% trans 'Refresh node hardware info' %}</a></li>
<li id="menu_test_connective" class="btn-test-connective" tabindex="-1"><a>{% trans 'Test node connective' %}</a></li>
<li class="divider"></li> <li class="divider"></li>
<li id="m_create" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Add node' %}</a></li> <li id="m_create" tabindex="-1" onclick="addTreeNode();"><a>{% trans 'Add node' %}</a></li>
<li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>{% trans 'Rename node' %}</a></li> <li id="m_del" tabindex="-1" onclick="editTreeNode();"><a>{% trans 'Rename node' %}</a></li>
@ -476,6 +478,49 @@ $(document).ready(function(){
} }
window.open(url, '_self'); window.open(url, '_self');
}) })
.on('click', '.btn-refresh-hardware', function () {
var url = "{% url 'api-assets:node-refresh-hardware-info' pk=DEFAULT_PK %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
} else {
return null;
}
var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id);
function success() {
rMenu.css({"visibility" : "hidden"});
}
APIUpdateAttr({
url: the_url,
method: "GET",
success_message: "更新硬件信息任务下发成功",
success: success
});
})
.on('click', '.btn-test-connective', function () {
var url = "{% url 'api-assets:node-test-connective' pk=DEFAULT_PK %}";
var nodes = zTree.getSelectedNodes();
var current_node;
if (nodes && nodes.length ===1 ){
current_node = nodes[0];
} else {
return null;
}
var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id);
function success() {
rMenu.css({"visibility" : "hidden"});
}
APIUpdateAttr({
url: the_url,
method: "GET",
success_message: "测试可连接性任务下发成功",
success: success
});
})
.on('click', '.btn_asset_delete', function () { .on('click', '.btn_asset_delete', function () {
var $this = $(this); var $this = $(this);
var $data_table = $("#asset_list_table").DataTable(); var $data_table = $("#asset_list_table").DataTable();

View File

@ -47,6 +47,8 @@ urlpatterns = [
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'),
] ]
urlpatterns += router.urls urlpatterns += router.urls

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n" "Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-07 21:18+0800\n" "POT-Creation-Date: 2018-03-12 11:28+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n" "Language-Team: Jumpserver team<ibuler@qq.com>\n"
@ -17,10 +17,18 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: assets/api/node.py:55 #: assets/api/node.py:58
msgid "New node {}" msgid "New node {}"
msgstr "新节点 {}" msgstr "新节点 {}"
#: assets/api/node.py:133
msgid "Refresh node assets hardware info: {}"
msgstr "更新一些资产硬件信息: {}"
#: assets/api/node.py:146
msgid "Test node assets connective: {}"
msgstr "测试节点资产可连接性"
#: assets/forms/asset.py:23 assets/forms/asset.py:54 assets/forms/user.py:125 #: assets/forms/asset.py:23 assets/forms/asset.py:54 assets/forms/user.py:125
#: assets/models/asset.py:53 assets/models/user.py:218 #: assets/models/asset.py:53 assets/models/user.py:218
#: assets/templates/assets/asset_detail.html:181 #: assets/templates/assets/asset_detail.html:181
@ -52,7 +60,7 @@ msgstr ""
"不支持ansible, 任意设置一个即可" "不支持ansible, 任意设置一个即可"
#: assets/forms/asset.py:80 assets/forms/asset.py:84 assets/forms/label.py:15 #: assets/forms/asset.py:80 assets/forms/asset.py:84 assets/forms/label.py:15
#: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:242 #: perms/templates/perms/asset_permission_asset.html:88 users/forms.py:244
msgid "Select assets" msgid "Select assets"
msgstr "选择资产" msgstr "选择资产"
@ -89,7 +97,7 @@ msgid "Password or private key passphrase"
msgstr "密码或密钥密码" msgstr "密码或密钥密码"
#: assets/forms/user.py:25 assets/models/user.py:30 common/forms.py:113 #: assets/forms/user.py:25 assets/models/user.py:30 common/forms.py:113
#: users/forms.py:16 users/forms.py:24 users/templates/users/login.html:59 #: users/forms.py:16 users/forms.py:25 users/templates/users/login.html:59
#: users/templates/users/reset_password.html:52 #: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11 #: users/templates/users/user_create.html:11
#: users/templates/users/user_password_update.html:40 #: users/templates/users/user_password_update.html:40
@ -358,7 +366,7 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:32
#: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33 #: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:190 #: terminal/templates/terminal/session_list.html:71 users/forms.py:192
#: users/models/user.py:30 users/models/user.py:254 #: users/models/user.py:30 users/models/user.py:254
#: users/templates/users/user_group_detail.html:78 #: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:333 #: users/templates/users/user_group_list.html:13 users/views/user.py:333
@ -424,43 +432,45 @@ msgstr "系统用户"
msgid "%(value)s is not an even number" msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number" msgstr "%(value)s is not an even number"
#: assets/tasks.py:92 #: assets/tasks.py:94
msgid "Update some assets hardware info" msgid "Update some assets hardware info"
msgstr "更新一些资产硬件信息" msgstr "更新一些资产硬件信息"
#: assets/tasks.py:108 #: assets/tasks.py:110
msgid "Update asset hardware info" msgid "Update asset hardware info"
msgstr "更新资产硬件信息" msgstr "更新资产硬件信息"
#: assets/tasks.py:122 #: assets/tasks.py:128
msgid "Update assets hardware info period" msgid "Update assets hardware info period"
msgstr "定期更新资产硬件信息" msgstr "定期更新资产硬件信息"
#: assets/tasks.py:195 #: assets/tasks.py:205
msgid "Test admin user connectability period: {}" msgid "Test admin user connectability period: {}"
msgstr "定期测试管理用户可连接性: {}" msgstr "定期测试管理用户可连接性: {}"
#: assets/tasks.py:201 #: assets/tasks.py:211
msgid "Test admin user connectability: {}" msgid "Test admin user connectability: {}"
msgstr "测试管理用户可连接性: {}" msgstr "测试管理用户可连接性: {}"
#: assets/tasks.py:210 #: assets/tasks.py:220
msgid "Test asset connectability" #, fuzzy
#| msgid "Test asset connectability"
msgid "Test assets connectability"
msgstr "测试资产可连接性" msgstr "测试资产可连接性"
#: assets/tasks.py:281 #: assets/tasks.py:290
msgid "Test system user connectability: {}" msgid "Test system user connectability: {}"
msgstr "测试系统用户可连接性: {}" msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:292 #: assets/tasks.py:305
msgid "test system user connectability period: {}" msgid "test system user connectability period: {}"
msgstr "测试系统用户可连接性: {}" msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:369 #: assets/tasks.py:382
msgid "Push system user to node: {} => {}" msgid "Push system user to node: {} => {}"
msgstr "推送系统用户到节点: {}->{}" msgstr "推送系统用户到节点: {}->{}"
#: assets/tasks.py:407 #: assets/tasks.py:420
msgid "Push system users to node: {}" msgid "Push system users to node: {}"
msgstr "推送系统用户到节点: {}" msgstr "推送系统用户到节点: {}"
@ -627,7 +637,7 @@ msgstr "提交"
#: assets/templates/assets/admin_user_detail.html:24 #: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:84 #: assets/templates/assets/admin_user_list.html:84
#: assets/templates/assets/asset_detail.html:24 #: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_list.html:166 #: assets/templates/assets/asset_list.html:168
#: assets/templates/assets/label_list.html:38 #: assets/templates/assets/label_list.html:38
#: assets/templates/assets/system_user_detail.html:26 #: assets/templates/assets/system_user_detail.html:26
#: assets/templates/assets/system_user_list.html:85 #: assets/templates/assets/system_user_list.html:85
@ -646,7 +656,7 @@ msgstr "更新"
#: assets/templates/assets/admin_user_detail.html:28 #: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:85 #: assets/templates/assets/admin_user_list.html:85
#: assets/templates/assets/asset_detail.html:28 #: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_list.html:167 #: assets/templates/assets/asset_list.html:169
#: assets/templates/assets/label_list.html:39 #: assets/templates/assets/label_list.html:39
#: assets/templates/assets/system_user_detail.html:30 #: assets/templates/assets/system_user_detail.html:30
#: assets/templates/assets/system_user_list.html:86 #: assets/templates/assets/system_user_list.html:86
@ -764,7 +774,7 @@ msgstr "替换资产的管理员"
#: assets/templates/assets/admin_user_detail.html:100 #: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:198 #: assets/templates/assets/asset_detail.html:198
#: assets/templates/assets/asset_list.html:541 #: assets/templates/assets/asset_list.html:586
#: assets/templates/assets/system_user_detail.html:183 #: assets/templates/assets/system_user_detail.html:183
#: assets/templates/assets/system_user_list.html:135 templates/_modal.html:16 #: assets/templates/assets/system_user_list.html:135 templates/_modal.html:16
#: terminal/templates/terminal/session_detail.html:108 #: terminal/templates/terminal/session_detail.html:108
@ -869,27 +879,35 @@ msgstr "从节点移除"
msgid "Add asset" msgid "Add asset"
msgstr "添加资产到节点" msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:122
msgid "Refresh node hardware info"
msgstr "更新节点资产硬件信息"
#: assets/templates/assets/asset_list.html:123 #: assets/templates/assets/asset_list.html:123
msgid "Test node connective"
msgstr "测试节点资产可连接性"
#: assets/templates/assets/asset_list.html:125
msgid "Add node" msgid "Add node"
msgstr "新建节点" msgstr "新建节点"
#: assets/templates/assets/asset_list.html:124 #: assets/templates/assets/asset_list.html:126
msgid "Rename node" msgid "Rename node"
msgstr "重命名节点" msgstr "重命名节点"
#: assets/templates/assets/asset_list.html:126 #: assets/templates/assets/asset_list.html:128
msgid "Delete node" msgid "Delete node"
msgstr "删除节点" msgstr "删除节点"
#: assets/templates/assets/asset_list.html:201 #: assets/templates/assets/asset_list.html:203
msgid "Create node failed" msgid "Create node failed"
msgstr "创建节点失败" msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:214 #: assets/templates/assets/asset_list.html:216
msgid "Have child node, cancel" msgid "Have child node, cancel"
msgstr "存在子节点,不能删除" msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:536 #: assets/templates/assets/asset_list.html:581
#: assets/templates/assets/system_user_list.html:130 #: assets/templates/assets/system_user_list.html:130
#: users/templates/users/user_detail.html:334 #: users/templates/users/user_detail.html:334
#: users/templates/users/user_detail.html:359 #: users/templates/users/user_detail.html:359
@ -898,20 +916,20 @@ msgstr "存在子节点,不能删除"
msgid "Are you sure?" msgid "Are you sure?"
msgstr "你确认吗?" msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:537 #: assets/templates/assets/asset_list.html:582
msgid "This will delete the selected assets !!!" msgid "This will delete the selected assets !!!"
msgstr "删除选择资产" msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:545 #: assets/templates/assets/asset_list.html:590
msgid "Asset Deleted." msgid "Asset Deleted."
msgstr "已被删除" msgstr "已被删除"
#: assets/templates/assets/asset_list.html:546 #: assets/templates/assets/asset_list.html:591
#: assets/templates/assets/asset_list.html:551 #: assets/templates/assets/asset_list.html:596
msgid "Asset Delete" msgid "Asset Delete"
msgstr "删除" msgstr "删除"
#: assets/templates/assets/asset_list.html:550 #: assets/templates/assets/asset_list.html:595
msgid "Asset Deleting failed." msgid "Asset Deleting failed."
msgstr "删除失败" msgstr "删除失败"
@ -1598,7 +1616,7 @@ msgstr "添加"
msgid "Add asset group to this permission" msgid "Add asset group to this permission"
msgstr "添加资产组" msgstr "添加资产组"
#: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:245 #: perms/templates/perms/asset_permission_asset.html:116 users/forms.py:247
msgid "Select asset groups" msgid "Select asset groups"
msgstr "选择资产组" msgstr "选择资产组"
@ -1627,7 +1645,7 @@ msgstr "资产组数量"
msgid "System user count" msgid "System user count"
msgstr "系统用户数量" msgstr "系统用户数量"
#: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:248 #: perms/templates/perms/asset_permission_detail.html:144 users/forms.py:250
msgid "Select system users" msgid "Select system users"
msgstr "选择系统用户" msgstr "选择系统用户"
@ -2061,51 +2079,59 @@ msgstr ""
msgid "Invalid token or cache refreshed." msgid "Invalid token or cache refreshed."
msgstr "" msgstr ""
#: users/forms.py:43 users/templates/users/user_detail.html:187 #: users/forms.py:28 users/models/user.py:38
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
#: users/templates/users/user_profile.html:55
msgid "Role"
msgstr "角色"
#: users/forms.py:45 users/templates/users/user_detail.html:187
msgid "Join user groups" msgid "Join user groups"
msgstr "添加到用户组" msgstr "添加到用户组"
#: users/forms.py:74 #: users/forms.py:76
msgid "Old password" msgid "Old password"
msgstr "原来密码" msgstr "原来密码"
#: users/forms.py:79 #: users/forms.py:81
msgid "New password" msgid "New password"
msgstr "新密码" msgstr "新密码"
#: users/forms.py:84 #: users/forms.py:86
msgid "Confirm password" msgid "Confirm password"
msgstr "确认密码" msgstr "确认密码"
#: users/forms.py:94 #: users/forms.py:96
msgid "Old password error" msgid "Old password error"
msgstr "原来密码错误" msgstr "原来密码错误"
#: users/forms.py:102 #: users/forms.py:104
msgid "Password does not match" msgid "Password does not match"
msgstr "密码不一致" msgstr "密码不一致"
#: users/forms.py:114 #: users/forms.py:116
msgid "ssh public key" msgid "ssh public key"
msgstr "ssh公钥" msgstr "ssh公钥"
#: users/forms.py:115 #: users/forms.py:117
msgid "ssh-rsa AAAA..." msgid "ssh-rsa AAAA..."
msgstr "" msgstr ""
#: users/forms.py:116 #: users/forms.py:118
msgid "Paste your id_rsa.pub here." msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里" msgstr "复制你的公钥到这里"
#: users/forms.py:129 #: users/forms.py:131
msgid "Public key should not be the same as your old one." msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同" msgstr "不能和原来的密钥相同"
#: users/forms.py:133 users/serializers.py:42 #: users/forms.py:135 users/serializers.py:42
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法" msgstr "ssh密钥不合法"
#: users/forms.py:147 users/forms.py:152 users/forms.py:164 users/forms.py:194 #: users/forms.py:149 users/forms.py:154 users/forms.py:166 users/forms.py:196
msgid "Select users" msgid "Select users"
msgstr "选择用户" msgstr "选择用户"
@ -2146,13 +2172,6 @@ msgstr "应用程序"
msgid "Email" msgid "Email"
msgstr "邮件" msgstr "邮件"
#: users/models/user.py:38 users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
#: users/templates/users/user_profile.html:55
msgid "Role"
msgstr "角色"
#: users/models/user.py:39 #: users/models/user.py:39
msgid "Avatar" msgid "Avatar"
msgstr "头像" msgstr "头像"