Unverified Commit d1a2fe14 authored by liuzheng712's avatar liuzheng712

Merge branch 'dev' of github.com:jumpserver/jumpserver into dev

parents 83f83d9b c4afd04c
......@@ -30,3 +30,4 @@ celerybeat.pid
django.db
celerybeat-schedule.db
data/static
_build/
......@@ -12,7 +12,7 @@ __all__ = ['Node']
class Node(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
value = models.CharField(max_length=128, unique=True, verbose_name=_("Value"))
value = models.CharField(max_length=128, verbose_name=_("Value"))
child_mark = models.IntegerField(default=0)
date_create = models.DateTimeField(auto_now_add=True)
......
......@@ -26,14 +26,14 @@ signer = get_signer()
class AssetUser(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
username = models.CharField(max_length=16, verbose_name=_('Username'))
username = models.CharField(max_length=128, verbose_name=_('Username'))
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
_public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
comment = models.TextField(blank=True, verbose_name=_('Comment'))
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by'))
created_by = models.CharField(max_length=128, null=True, verbose_name=_('Created by'))
@property
def password(self):
......
......@@ -91,7 +91,7 @@ def update_assets_hardware_info_util(assets, task_name=None):
if task_name is None:
task_name = _("Update some assets hardware info")
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
hostname_list = [asset.hostname for asset in assets]
hostname_list = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()]
task, created = update_or_create_ansible_task(
task_name, hosts=hostname_list, tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, run_as_admin=True, created_by='System',
......@@ -120,7 +120,10 @@ def update_assets_hardware_info_period():
"""
from ops.utils import update_or_create_ansible_task
task_name = _("Update assets hardware info period")
hostname_list = [asset.hostname for asset in Asset.objects.all()]
hostname_list = [
asset.hostname for asset in Asset.objects.all()
if asset.is_active and asset.is_unixlike()
]
tasks = const.UPDATE_ASSETS_HARDWARE_TASKS
# Only create, schedule by celery beat
......@@ -165,7 +168,8 @@ def test_admin_user_connectability_util(admin_user, task_name):
from ops.utils import update_or_create_ansible_task
assets = admin_user.get_related_assets()
hosts = [asset.hostname for asset in assets]
hosts = [asset.hostname for asset in assets
if asset.is_active and asset.is_unixlike()]
if not hosts:
return
tasks = const.TEST_ADMIN_USER_CONN_TASKS
......@@ -257,7 +261,7 @@ def test_system_user_connectability_util(system_user, task_name):
"""
from ops.utils import update_or_create_ansible_task
assets = system_user.assets
hosts = [asset.hostname for asset in assets]
hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()]
tasks = const.TEST_SYSTEM_USER_CONN_TASKS
if not hosts:
logger.info("No hosts, passed")
......@@ -346,7 +350,7 @@ def push_system_user_util(system_users, assets, task_name):
logger.info("Not tasks, passed")
return {}
hosts = [asset.hostname for asset in assets]
hosts = [asset.hostname for asset in assets if asset.is_active and asset.is_unixlike()]
if not hosts:
logger.info("Not hosts, passed")
return {}
......
......@@ -13,7 +13,7 @@
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create system user' %}</h5>
<h5>{{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......@@ -81,6 +81,14 @@
{% block custom_foot_js %}
<script>
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
var protocol_id = '#' + '{{ form.protocol.id_for_label }}';
var password_id = '#' + '{{ form.password.id_for_label }}';
var private_key_id = '#' + '{{ form.private_key_file.id_for_label }}';
var sudo_id = '#' + '{{ form.sudo.id_for_label }}';
var shell_id = '#' + '{{ form.shell.id_for_label }}';
var need_change_field = [auto_generate_key, private_key_id, sudo_id, shell_id] ;
function authFieldsDisplay() {
if ($(auto_generate_key).prop('checked')) {
$('.auth-fields').addClass('hidden');
......@@ -88,9 +96,23 @@
$('.auth-fields').removeClass('hidden');
}
}
function protocolChange() {
if ($(protocol_id).attr('value') === 'rdp') {
$.each(need_change_field, function (index, value) {
$(value).addClass('hidden')
});
$(password_id).removeClass('hidden')
} else {
$.each(need_change_field, function (index, value) {
$(value).removeClass('hidden')
});
}
}
$(document).ready(function () {
$('.select2').select2();
authFieldsDisplay();
protocolChange();
$(auto_generate_key).change(function () {
authFieldsDisplay();
});
......
......@@ -13,7 +13,7 @@
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create admin user' %}</h5>
<h5>{{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......
......@@ -2,6 +2,12 @@
{% load static %}
{% load i18n %}
{% block help_message %}
<div class="alert alert-info help-message">
左侧是资产树,右击可以新建、删除、更改树节点,授权资产也是以节点方式组织的,右侧是属于该节点下的资产
</div>
{% endblock %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
......@@ -224,7 +230,9 @@ function editTreeNode() {
if (!current_node){
return
}
current_node.name = current_node.value;
if (current_node.value) {
current_node.name = current_node.value;
}
zTree.editName(current_node);
}
......@@ -313,38 +321,36 @@ function beforeDrag() {
return true
}
function beforeDrop() {
return true
function beforeDrop(treeId, treeNodes, targetNode, moveType) {
var treeNodesNames = [];
$.each(treeNodes, function (index, value) {
treeNodesNames.push(value.value);
});
var msg = "你想移动节点: `" + treeNodesNames.join(",") + "` 到 `" + targetNode.value + "` 下吗?";
if (confirm(msg)){
return true
} else {
return false
}
}
function onDrag(event, treeId, treeNodes) {
}
function onDrop(event, treeId, treeNodes, targetNode, moveType) {
console.log("DROP");
console.log(event);
console.log(treeNodes);
console.log(targetNode);
console.log(moveType);
var treeNodesNames = [];
var treeNodesIds = [];
$.each(treeNodes, function (index, value) {
treeNodesNames.push(value.value);
treeNodesIds.push(value.id);
});
var msg = "你想移动节点: `" + treeNodesNames.join(",") + "` 到 `" + targetNode.value + "` 下吗?";
var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.id);
var body = {nodes: treeNodesIds};
if (confirm(msg)){
APIUpdateAttr({
url: the_url,
method: "PUT",
body: JSON.stringify(body)
})
}
APIUpdateAttr({
url: the_url,
method: "PUT",
body: JSON.stringify(body)
})
}
function initTree() {
......
......@@ -178,7 +178,7 @@
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-sm" id="btn-add-to-cluster">{% trans 'Confirm' %}</button>
<button type="button" class="btn btn-info btn-sm" id="btn-add-to-node">{% trans 'Confirm' %}</button>
</td>
</tr>
</form>
......@@ -187,7 +187,7 @@
<tr>
<td ><b class="bdg_node" data-gid={{ node.id }}>{{ node.name }}</b></td>
<td>
<button class="btn btn-danger pull-right btn-xs btn-remove-from-cluster" type="button"><i class="fa fa-minus"></i></button>
<button class="btn btn-danger pull-right btn-xs btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
......@@ -204,27 +204,27 @@
{% endblock %}
{% block custom_foot_js %}
<script>
function updateSystemUserCluster(clusters) {
function updateSystemUserCluster(nodes) {
var the_url = "{% url 'api-assets:system-user-detail' pk=system_user.id %}";
var body = {
cluster: Object.assign([], clusters)
nodes: Object.assign([], nodes)
};
var success = function(data) {
// remove all the selected groups from select > option and rendered ul element;
$('.select2-selection__rendered').empty();
$('#cluster_selected').val('');
$.map(jumpserver.cluster_selected, function(cluster_name, index) {
$.map(jumpserver.nodes_selected, function(cluster_name, index) {
$('#opt_' + index).remove();
// change tr html of user groups.
$('.cluster_edit tbody').append(
'<tr>' +
'<td><b class="bdg_node" data-gid="' + index + '">' + cluster_name + '</b></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-from-cluster" type="button"><i class="fa fa-minus"></i></button></td>' +
'<td><button class="btn btn-danger btn-xs pull-right btn-remove-from-node" type="button"><i class="fa fa-minus"></i></button></td>' +
'</tr>'
)
});
// clear jumpserver.groups_selected
jumpserver.cluster_selected = {};
jumpserver.nodes_selected = {};
};
APIUpdateAttr({
url: the_url,
......@@ -232,16 +232,16 @@ function updateSystemUserCluster(clusters) {
success: success
});
}
jumpserver.cluster_selected = {};
jumpserver.nodes_selected = {};
$(document).ready(function () {
$('.select2').select2()
.on('select2:select', function(evt) {
var data = evt.params.data;
jumpserver.cluster_selected[data.id] = data.text;
jumpserver.nodes_selected[data.id] = data.text;
})
.on('select2:unselect', function(evt) {
var data = evt.params.data;
delete jumpserver.cluster_selected[data.id];
delete jumpserver.nodes_selected[data.id];
});
})
.on('click', '#btn-auto-push', function () {
......@@ -255,26 +255,26 @@ $(document).ready(function () {
body: JSON.stringify(body)
});
})
.on('click', '#btn-add-to-cluster', function() {
if (Object.keys(jumpserver.cluster_selected).length === 0) {
.on('click', '#btn-add-to-node', function() {
if (Object.keys(jumpserver.nodes_selected).length === 0) {
return false;
}
var clusters = $('.bdg_node').map(function() {
var nodes = $('.bdg_node').map(function() {
return $(this).data('gid');
}).get();
$.map(jumpserver.cluster_selected, function(value, index) {
clusters.push(index);
$.map(jumpserver.nodes_selected, function(value, index) {
nodes.push(index);
});
updateSystemUserCluster(clusters);
updateSystemUserCluster(nodes);
})
.on('click', '.btn-remove-from-cluster', function() {
.on('click', '.btn-remove-from-node', function() {
var $this = $(this);
var $tr = $this.closest('tr');
var $badge = $tr.find('.bdg_node');
var gid = $badge.data('gid');
var cluster_name = $badge.html() || $badge.text();
var node_name = $badge.html() || $badge.text();
$('#groups_selected').append(
'<option value="' + gid + '" id="opt_' + gid + '">' + cluster_name + '</option>'
'<option value="' + gid + '" id="opt_' + gid + '">' + node_name + '</option>'
);
$tr.remove();
var clusters = $('.bdg_node').map(function () {
......
......@@ -58,8 +58,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Asset list'),
'action': _('My assets'),
'system_users': SystemUser.objects.all(),
}
kwargs.update(context)
......
......@@ -97,6 +97,9 @@ class LDAPTestingAPI(APIView):
class DjangoSettingsAPI(APIView):
def get(self, request):
if not settings.DEBUG:
return Response('Only debug mode support')
configs = {}
for i in dir(settings):
if i.isupper():
......
......@@ -99,9 +99,8 @@ class DatetimeSearchMixin:
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, self.date_format)
self.date_from = date_from.replace(
tzinfo=timezone.get_current_timezone()
)
tz = timezone.get_current_timezone()
self.date_from = tz.localize(date_from)
else:
self.date_from = timezone.now() - timezone.timedelta(7)
......
......@@ -73,17 +73,20 @@ def to_html(s):
@register.filter
def time_util_with_seconds(date_from, date_to):
if date_from and date_to:
delta = date_to - date_from
seconds = delta.seconds
if seconds < 60:
return '{} s'.format(seconds)
elif seconds < 60*60:
return '{} m'.format(seconds//60)
else:
return '{} h'.format(seconds//3600)
else:
if not date_from:
return ''
if not date_to:
return ''
date_to = timezone.now()
delta = date_to - date_from
seconds = delta.seconds
if seconds < 60:
return '{} s'.format(seconds)
elif seconds < 60*60:
return '{} m'.format(seconds//60)
else:
return '{} h'.format(seconds//3600)
@register.filter
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-02-26 16:54+0800\n"
"POT-Creation-Date: 2018-03-07 11:54+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,12 +17,12 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: assets/api/node.py:54
#: assets/api/node.py:55
msgid "New node {}"
msgstr "新节点 {}"
#: assets/forms/asset.py:23 assets/forms/asset.py:52 assets/forms/user.py:125
#: assets/models/asset.py:45 assets/models/user.py:221
#: assets/models/asset.py:53 assets/models/user.py:218
#: assets/templates/assets/asset_detail.html:181
#: assets/templates/assets/asset_detail.html:189
#: assets/templates/assets/system_user_detail.html:164
......@@ -30,16 +30,16 @@ msgid "Nodes"
msgstr "节点管理"
#: assets/forms/asset.py:26 assets/forms/asset.py:55 assets/forms/asset.py:90
#: assets/forms/asset.py:94 assets/models/asset.py:49
#: assets/models/cluster.py:19 assets/models/user.py:190
#: assets/templates/assets/asset_detail.html:73 templates/_nav.html:23
#: assets/forms/asset.py:94 assets/models/asset.py:57
#: assets/models/cluster.py:19 assets/models/user.py:187
#: assets/templates/assets/asset_detail.html:73 templates/_nav.html:24
msgid "Admin user"
msgstr "管理用户"
#: assets/forms/asset.py:29 assets/forms/asset.py:58 assets/models/asset.py:73
#: assets/templates/assets/asset_create.html:31
#: assets/forms/asset.py:29 assets/forms/asset.py:58 assets/models/asset.py:81
#: assets/templates/assets/asset_create.html:32
#: assets/templates/assets/asset_detail.html:218
#: assets/templates/assets/asset_update.html:36 templates/_nav.html:25
#: assets/templates/assets/asset_update.html:37 templates/_nav.html:26
msgid "Labels"
msgstr "标签管理"
......@@ -54,7 +54,7 @@ msgstr "管理用户是资产上已经存在的特权用户,如 root或者其
msgid "Select assets"
msgstr "选择资产"
#: assets/forms/asset.py:86 assets/models/asset.py:44
#: assets/forms/asset.py:86 assets/models/asset.py:52
#: assets/templates/assets/admin_user_assets.html:53
#: assets/templates/assets/asset_detail.html:69
#: assets/templates/assets/system_user_asset.html:51
......@@ -62,7 +62,7 @@ msgstr "选择资产"
msgid "Port"
msgstr "端口"
#: assets/forms/asset.py:106 assets/templates/assets/asset_create.html:35
#: assets/forms/asset.py:106 assets/templates/assets/asset_create.html:36
msgid "Select labels"
msgstr "选择标签"
......@@ -70,11 +70,11 @@ msgstr "选择标签"
msgid "Select nodes"
msgstr "选择节点"
#: assets/forms/label.py:13 assets/models/asset.py:137
#: assets/forms/label.py:13 assets/models/asset.py:153
#: assets/templates/assets/admin_user_list.html:24
#: assets/templates/assets/label_list.html:16
#: assets/templates/assets/system_user_list.html:26 perms/models.py:17
#: terminal/backends/command/models.py:11 terminal/models.py:116
#: terminal/backends/command/models.py:11 terminal/models.py:123
#: terminal/templates/terminal/command_list.html:40
#: terminal/templates/terminal/command_list.html:73
#: terminal/templates/terminal/session_list.html:41
......@@ -84,10 +84,10 @@ msgstr "资产"
#: assets/forms/user.py:24
msgid "Password or private key passphrase"
msgstr "密码或钥密码"
msgstr "密码或钥密码"
#: 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:56
#: users/forms.py:16 users/forms.py:24 users/templates/users/login.html:59
#: users/templates/users/reset_password.html:52
#: users/templates/users/user_create.html:11
#: users/templates/users/user_password_update.html:40
......@@ -116,11 +116,11 @@ msgstr "密码和私钥, 必须输入一个"
#: assets/templates/assets/system_user_detail.html:58
#: assets/templates/assets/system_user_list.html:24 common/models.py:26
#: common/templates/common/terminal_setting.html:67
#: common/templates/common/terminal_setting.html:88 ops/models.py:31
#: common/templates/common/terminal_setting.html:85 ops/models.py:31
#: ops/templates/ops/task_detail.html:56 ops/templates/ops/task_list.html:34
#: perms/models.py:14 perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:15
#: terminal/models.py:141 terminal/templates/terminal/terminal_detail.html:43
#: perms/templates/perms/asset_permission_user.html:54 terminal/models.py:16
#: terminal/models.py:149 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
#: users/models/user.py:35 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63
......@@ -138,9 +138,9 @@ msgstr "名称"
#: assets/templates/assets/system_user_detail.html:62
#: assets/templates/assets/system_user_list.html:25
#: perms/templates/perms/asset_permission_user.html:55 users/forms.py:14
#: users/models/authentication.py:44 users/models/user.py:34
#: users/models/authentication.py:45 users/models/user.py:34
#: users/templates/users/_select_user_modal.html:14
#: users/templates/users/login.html:53
#: users/templates/users/login.html:56
#: users/templates/users/login_log_list.html:49
#: users/templates/users/user_detail.html:67
#: users/templates/users/user_list.html:24
......@@ -162,10 +162,10 @@ msgid ""
"than 2 system user"
msgstr "高优先级的系统用户将会作为默认登录用户"
#: assets/models/asset.py:42 assets/templates/assets/_asset_list_modal.html:21
#: assets/models/asset.py:50 assets/templates/assets/_asset_list_modal.html:21
#: assets/templates/assets/admin_user_assets.html:52
#: assets/templates/assets/asset_detail.html:61
#: assets/templates/assets/asset_list.html:81
#: assets/templates/assets/asset_list.html:87
#: assets/templates/assets/system_user_asset.html:50
#: assets/templates/assets/user_asset_list.html:20 common/forms.py:144
#: perms/templates/perms/asset_permission_asset.html:55
......@@ -175,10 +175,10 @@ msgstr "高优先级的系统用户将会作为默认登录用户"
msgid "IP"
msgstr "IP"
#: assets/models/asset.py:43 assets/templates/assets/_asset_list_modal.html:20
#: assets/models/asset.py:51 assets/templates/assets/_asset_list_modal.html:20
#: assets/templates/assets/admin_user_assets.html:51
#: assets/templates/assets/asset_detail.html:57
#: assets/templates/assets/asset_list.html:80
#: assets/templates/assets/asset_list.html:86
#: assets/templates/assets/system_user_asset.html:49
#: assets/templates/assets/user_asset_list.html:19 common/forms.py:143
#: perms/templates/perms/asset_permission_asset.html:54
......@@ -187,77 +187,77 @@ msgstr "IP"
msgid "Hostname"
msgstr "主机名"
#: assets/models/asset.py:46 assets/models/label.py:20
#: assets/models/asset.py:54 assets/models/label.py:20
#: assets/templates/assets/asset_detail.html:105
#: perms/templates/perms/asset_permission_list.html:70
msgid "Is active"
msgstr "激活"
#: assets/models/asset.py:52 assets/templates/assets/asset_detail.html:65
#: assets/models/asset.py:60 assets/templates/assets/asset_detail.html:65
msgid "Public IP"
msgstr "公网IP"
#: assets/models/asset.py:53 assets/templates/assets/asset_detail.html:113
#: assets/models/asset.py:61 assets/templates/assets/asset_detail.html:113
msgid "Asset number"
msgstr "资产编号"
#: assets/models/asset.py:56 assets/templates/assets/asset_detail.html:77
#: assets/models/asset.py:64 assets/templates/assets/asset_detail.html:77
msgid "Vendor"
msgstr "制造商"
#: assets/models/asset.py:57 assets/templates/assets/asset_detail.html:81
#: assets/models/asset.py:65 assets/templates/assets/asset_detail.html:81
msgid "Model"
msgstr "型号"
#: assets/models/asset.py:58 assets/templates/assets/asset_detail.html:109
#: assets/models/asset.py:66 assets/templates/assets/asset_detail.html:109
msgid "Serial number"
msgstr "序列号"
#: assets/models/asset.py:60
#: assets/models/asset.py:68
msgid "CPU model"
msgstr "CPU型号"
#: assets/models/asset.py:61
#: assets/models/asset.py:69
msgid "CPU count"
msgstr "CPU数量"
#: assets/models/asset.py:62
#: assets/models/asset.py:70
msgid "CPU cores"
msgstr "CPU核数"
#: assets/models/asset.py:63 assets/templates/assets/asset_detail.html:89
#: assets/models/asset.py:71 assets/templates/assets/asset_detail.html:89
msgid "Memory"
msgstr "内存"
#: assets/models/asset.py:64
#: assets/models/asset.py:72
msgid "Disk total"
msgstr "硬盘大小"
#: assets/models/asset.py:65
#: assets/models/asset.py:73
msgid "Disk info"
msgstr "硬盘信息"
#: assets/models/asset.py:67 assets/templates/assets/asset_detail.html:97
#: assets/models/asset.py:75 assets/templates/assets/asset_detail.html:97
msgid "Platform"
msgstr "系统平台"
#: assets/models/asset.py:68 assets/templates/assets/asset_detail.html:101
#: assets/models/asset.py:76 assets/templates/assets/asset_detail.html:101
msgid "OS"
msgstr "操作系统"
#: assets/models/asset.py:69
#: assets/models/asset.py:77
msgid "OS version"
msgstr "系统版本"
#: assets/models/asset.py:70
#: assets/models/asset.py:78
msgid "OS arch"
msgstr "系统架构"
#: assets/models/asset.py:71
#: assets/models/asset.py:79
msgid "Hostname raw"
msgstr "主机名原始"
#: assets/models/asset.py:74 assets/models/cluster.py:28
#: assets/models/asset.py:82 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:117
......@@ -268,7 +268,7 @@ msgstr "主机名原始"
msgid "Created by"
msgstr "创建者"
#: assets/models/asset.py:75 assets/models/cluster.py:26
#: assets/models/asset.py:83 assets/models/cluster.py:26
#: assets/models/group.py:22 assets/models/label.py:23
#: assets/templates/assets/admin_user_detail.html:64
#: assets/templates/assets/system_user_detail.html:92
......@@ -280,7 +280,7 @@ msgstr "创建者"
msgid "Date created"
msgstr "创建日期"
#: assets/models/asset.py:76 assets/models/cluster.py:29
#: assets/models/asset.py:84 assets/models/cluster.py:29
#: assets/models/group.py:23 assets/models/label.py:21 assets/models/user.py:33
#: assets/templates/assets/admin_user_detail.html:72
#: assets/templates/assets/admin_user_list.html:28
......@@ -288,7 +288,7 @@ msgstr "创建日期"
#: assets/templates/assets/system_user_detail.html:100
#: assets/templates/assets/system_user_list.html:30 common/models.py:30
#: ops/models.py:37 perms/models.py:24 perms/models.py:81
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:25
#: perms/templates/perms/asset_permission_detail.html:98 terminal/models.py:26
#: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15
#: users/models/user.py:47 users/templates/users/user_detail.html:111
#: users/templates/users/user_group_detail.html:67
......@@ -331,7 +331,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:13
#: users/models/user.py:261
#: users/models/user.py:266
msgid "System"
msgstr "系统"
......@@ -352,13 +352,14 @@ msgid "Default asset group"
msgstr "默认资产组"
#: assets/models/label.py:14 perms/models.py:15
#: terminal/backends/command/models.py:10 terminal/models.py:115
#: terminal/backends/command/models.py:10 terminal/models.py:122
#: 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/forms.py:190
#: users/models/user.py:30 users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:330
#: users/models/user.py:30 users/models/user.py:254
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:333
msgid "User"
msgstr "用户"
......@@ -383,32 +384,32 @@ msgstr "ssh密钥"
msgid "SSH public key"
msgstr "ssh公钥"
#: assets/models/user.py:222
#: assets/models/user.py:219
msgid "Priority"
msgstr "优先级"
#: assets/models/user.py:223 assets/templates/assets/system_user_detail.html:66
#: assets/models/user.py:220 assets/templates/assets/system_user_detail.html:66
msgid "Protocol"
msgstr "协议"
#: assets/models/user.py:224 assets/templates/assets/_system_user.html:58
#: assets/models/user.py:221 assets/templates/assets/_system_user.html:58
#: assets/templates/assets/system_user_detail.html:118
#: assets/templates/assets/system_user_update.html:11
msgid "Auto push"
msgstr "自动推送"
#: assets/models/user.py:225 assets/templates/assets/system_user_detail.html:70
#: assets/models/user.py:222 assets/templates/assets/system_user_detail.html:70
msgid "Sudo"
msgstr "Sudo"
#: assets/models/user.py:226 assets/templates/assets/system_user_detail.html:75
#: assets/models/user.py:223 assets/templates/assets/system_user_detail.html:75
msgid "Shell"
msgstr "Shell"
#: assets/models/user.py:269 perms/forms.py:25 perms/models.py:19
#: assets/models/user.py:266 perms/forms.py:25 perms/models.py:19
#: perms/models.py:76 perms/templates/perms/asset_permission_detail.html:136
#: perms/templates/perms/asset_permission_list.html:69 templates/_nav.html:24
#: terminal/backends/command/models.py:12 terminal/models.py:117
#: perms/templates/perms/asset_permission_list.html:69 templates/_nav.html:25
#: terminal/backends/command/models.py:12 terminal/models.py:124
#: terminal/templates/terminal/command_list.html:48
#: terminal/templates/terminal/command_list.html:74
#: terminal/templates/terminal/session_list.html:49
......@@ -433,37 +434,37 @@ msgstr "更新资产硬件信息"
msgid "Update assets hardware info period"
msgstr "定期更新资产硬件信息"
#: assets/tasks.py:191
#: assets/tasks.py:195
msgid "Test admin user connectability period: {}"
msgstr "定期测试管理用户可连接性: {}"
#: assets/tasks.py:197
#: assets/tasks.py:201
msgid "Test admin user connectability: {}"
msgstr "测试管理用户可连接性: {}"
#: assets/tasks.py:206
#: assets/tasks.py:210
msgid "Test asset connectability"
msgstr "测试资产可连接性"
#: assets/tasks.py:277
#: assets/tasks.py:281
msgid "Test system user connectability: {}"
msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:288
#: assets/tasks.py:292
msgid "test system user connectability period: {}"
msgstr "测试系统用户可连接性: {}"
#: assets/tasks.py:361
#: assets/tasks.py:365
msgid "Push system user to node: {} => {}"
msgstr "推送系统用户到节点: {}->{}"
#: assets/tasks.py:393
#: assets/tasks.py:397
msgid "Push system users to node: {}"
msgstr "推送系统用户到节点: {}"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:5
msgid "Update asset group"
msgstr "编辑用户组"
msgstr "更新用户组"
#: assets/templates/assets/_asset_group_bulk_update_modal.html:8
msgid "Hint: only change the field you want to update."
......@@ -474,12 +475,11 @@ msgstr "仅修改你需要更新的字段"
#: assets/views/admin_user.py:29 assets/views/admin_user.py:47
#: assets/views/admin_user.py:63 assets/views/admin_user.py:78
#: assets/views/admin_user.py:102 assets/views/asset.py:48
#: assets/views/asset.py:61 assets/views/asset.py:95 assets/views/asset.py:155
#: assets/views/asset.py:172 assets/views/asset.py:196 assets/views/label.py:26
#: assets/views/label.py:42 assets/views/label.py:58
#: assets/views/system_user.py:28 assets/views/system_user.py:44
#: assets/views/system_user.py:60 assets/views/system_user.py:74
#: templates/_nav.html:19
#: assets/views/asset.py:94 assets/views/asset.py:154 assets/views/asset.py:171
#: assets/views/asset.py:195 assets/views/label.py:26 assets/views/label.py:42
#: assets/views/label.py:58 assets/views/system_user.py:28
#: assets/views/system_user.py:44 assets/views/system_user.py:60
#: assets/views/system_user.py:74 templates/_nav.html:20
msgid "Assets"
msgstr "资产管理"
......@@ -522,14 +522,14 @@ msgid "If set id, will use this id update asset existed"
msgstr "如果设置了id,则会使用该行信息更新该id的资产"
#: assets/templates/assets/_asset_list_modal.html:22
#: assets/templates/assets/asset_list.html:82
#: assets/templates/assets/asset_list.html:88
#: assets/templates/assets/user_asset_list.html:22
msgid "Hardware"
msgstr "硬件"
#: assets/templates/assets/_asset_list_modal.html:23
#: assets/templates/assets/asset_detail.html:143
#: assets/templates/assets/asset_list.html:83
#: assets/templates/assets/asset_list.html:89
#: assets/templates/assets/user_asset_list.html:23 perms/models.py:20
#: perms/models.py:77
#: perms/templates/perms/asset_permission_create_update.html:51
......@@ -548,7 +548,7 @@ msgstr "激活中"
#: assets/templates/assets/admin_user_assets.html:54
#: assets/templates/assets/admin_user_list.html:25
#: assets/templates/assets/asset_detail.html:357
#: assets/templates/assets/asset_list.html:84
#: assets/templates/assets/asset_list.html:90
#: assets/templates/assets/system_user_asset.html:52
#: assets/templates/assets/system_user_list.html:27
#: users/templates/users/user_granted_asset.html:47
......@@ -558,13 +558,13 @@ msgstr "可连接"
#: assets/templates/assets/_asset_list_modal.html:25
#: assets/templates/assets/admin_user_list.html:29
#: assets/templates/assets/asset_list.html:85
#: assets/templates/assets/asset_list.html:91
#: assets/templates/assets/label_list.html:17
#: assets/templates/assets/system_user_list.html:31
#: 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:41
#: perms/templates/perms/asset_permission_list.html:72
#: terminal/templates/terminal/session_list.html:79
#: terminal/templates/terminal/session_list.html:80
#: terminal/templates/terminal/terminal_list.html:36
#: users/templates/users/user_group_list.html:15
#: users/templates/users/user_list.html:28
......@@ -572,25 +572,25 @@ msgid "Action"
msgstr "动作"
#: assets/templates/assets/_asset_list_modal.html:34
#: assets/templates/assets/asset_list.html:94
#: assets/templates/assets/asset_list.html:100
#: users/templates/users/user_list.html:37
msgid "Delete selected"
msgstr "批量删除"
#: assets/templates/assets/_asset_list_modal.html:35
#: assets/templates/assets/asset_list.html:95
#: assets/templates/assets/asset_list.html:101
#: users/templates/users/user_list.html:38
msgid "Update selected"
msgstr "批量更新"
#: assets/templates/assets/_asset_list_modal.html:36
#: assets/templates/assets/asset_list.html:97
#: assets/templates/assets/asset_list.html:103
#: users/templates/users/user_list.html:39
msgid "Deactive selected"
msgstr "禁用所选"
#: assets/templates/assets/_asset_list_modal.html:37
#: assets/templates/assets/asset_list.html:98
#: assets/templates/assets/asset_list.html:104
#: users/templates/users/user_list.html:40
msgid "Active selected"
msgstr "激活所选"
......@@ -599,16 +599,17 @@ msgstr "激活所选"
#: assets/templates/assets/_system_user.html:71
#: assets/templates/assets/admin_user_create_update.html:46
#: assets/templates/assets/asset_bulk_update.html:24
#: assets/templates/assets/asset_create.html:65
#: assets/templates/assets/asset_list.html:102
#: assets/templates/assets/asset_update.html:69
#: assets/templates/assets/asset_create.html:66
#: assets/templates/assets/asset_list.html:108
#: assets/templates/assets/asset_update.html:70
#: assets/templates/assets/label_create_update.html:17
#: common/templates/common/basic_setting.html:59
#: common/templates/common/email_setting.html:60
#: common/templates/common/ldap_setting.html:60
#: common/templates/common/terminal_setting.html:108
#: common/templates/common/terminal_setting.html:103
#: perms/templates/perms/asset_permission_create_update.html:72
#: terminal/templates/terminal/terminal_update.html:47
#: terminal/templates/terminal/session_list.html:120
#: terminal/templates/terminal/terminal_update.html:48
#: users/templates/users/_user.html:44
#: users/templates/users/first_login.html:62
#: users/templates/users/forgot_password.html:44
......@@ -624,7 +625,7 @@ msgstr "提交"
#: assets/templates/assets/admin_user_detail.html:24
#: assets/templates/assets/admin_user_list.html:84
#: assets/templates/assets/asset_detail.html:24
#: assets/templates/assets/asset_list.html:160
#: assets/templates/assets/asset_list.html:166
#: assets/templates/assets/label_list.html:38
#: assets/templates/assets/system_user_detail.html:26
#: assets/templates/assets/system_user_list.html:85
......@@ -643,7 +644,7 @@ msgstr "更新"
#: assets/templates/assets/admin_user_detail.html:28
#: assets/templates/assets/admin_user_list.html:85
#: assets/templates/assets/asset_detail.html:28
#: assets/templates/assets/asset_list.html:161
#: assets/templates/assets/asset_list.html:167
#: assets/templates/assets/label_list.html:39
#: assets/templates/assets/system_user_detail.html:30
#: assets/templates/assets/system_user_list.html:86
......@@ -659,12 +660,6 @@ msgstr "更新"
msgid "Delete"
msgstr "删除"
#: assets/templates/assets/_system_user.html:16
#: assets/templates/assets/system_user_list.html:16
#: assets/views/system_user.py:45
msgid "Create system user"
msgstr "创建系统用户"
#: assets/templates/assets/_system_user.html:37
#: assets/templates/assets/asset_create.html:16
#: assets/templates/assets/asset_update.html:21
......@@ -673,8 +668,8 @@ msgid "Basic"
msgstr "基本"
#: assets/templates/assets/_system_user.html:44
#: assets/templates/assets/asset_create.html:23
#: assets/templates/assets/asset_update.html:28
#: assets/templates/assets/asset_create.html:24
#: assets/templates/assets/asset_update.html:29
#: assets/templates/assets/system_user_update.html:7
#: users/templates/users/user_create.html:9
#: users/templates/users/user_update.html:6
......@@ -683,28 +678,28 @@ msgstr "认证"
#: assets/templates/assets/_system_user.html:47
msgid "Auto generate key"
msgstr "自动生成钥"
msgstr "自动生成钥"
#: assets/templates/assets/_system_user.html:64
#: assets/templates/assets/asset_create.html:57
#: assets/templates/assets/asset_update.html:61
#: assets/templates/assets/asset_create.html:58
#: assets/templates/assets/asset_update.html:62
#: perms/templates/perms/asset_permission_create_update.html:49
#: terminal/templates/terminal/terminal_update.html:41
#: terminal/templates/terminal/terminal_update.html:42
msgid "Other"
msgstr "其它"
#: assets/templates/assets/_system_user.html:70
#: assets/templates/assets/admin_user_create_update.html:45
#: assets/templates/assets/asset_bulk_update.html:23
#: assets/templates/assets/asset_create.html:64
#: assets/templates/assets/asset_update.html:68
#: assets/templates/assets/asset_create.html:65
#: assets/templates/assets/asset_update.html:69
#: assets/templates/assets/label_create_update.html:16
#: common/templates/common/basic_setting.html:58
#: common/templates/common/email_setting.html:59
#: common/templates/common/ldap_setting.html:59
#: common/templates/common/terminal_setting.html:106
#: common/templates/common/terminal_setting.html:101
#: perms/templates/perms/asset_permission_create_update.html:71
#: terminal/templates/terminal/terminal_update.html:46
#: terminal/templates/terminal/terminal_update.html:47
#: users/templates/users/_user.html:43
#: users/templates/users/user_bulk_update.html:23
#: users/templates/users/user_password_update.html:58
......@@ -761,19 +756,13 @@ msgstr "测试"
msgid "Task has been send, seen left asset status"
msgstr "任务已下发,查看左侧资产状态"
#: assets/templates/assets/admin_user_create_update.html:16
#: assets/templates/assets/admin_user_list.html:14
#: assets/views/admin_user.py:48
msgid "Create admin user"
msgstr "创建管理用户"
#: assets/templates/assets/admin_user_detail.html:83
msgid "Replace node assets admin user with this"
msgstr "替换资产的管理员"
#: assets/templates/assets/admin_user_detail.html:100
#: assets/templates/assets/asset_detail.html:198
#: assets/templates/assets/asset_list.html:482
#: assets/templates/assets/asset_list.html:541
#: assets/templates/assets/system_user_detail.html:181
#: assets/templates/assets/system_user_list.html:135 templates/_modal.html:16
#: terminal/templates/terminal/session_detail.html:108
......@@ -787,6 +776,11 @@ msgstr "替换资产的管理员"
msgid "Confirm"
msgstr "确认"
#: assets/templates/assets/admin_user_list.html:14
#: assets/views/admin_user.py:48
msgid "Create admin user"
msgstr "创建管理用户"
#: assets/templates/assets/admin_user_list.html:26
#: assets/templates/assets/system_user_list.html:28
msgid "Unreachable"
......@@ -799,20 +793,20 @@ msgstr "不可达"
msgid "Ratio"
msgstr "比例"
#: assets/templates/assets/asset_create.html:27
#: assets/templates/assets/asset_update.html:32 perms/models.py:74
#: assets/templates/assets/asset_create.html:28
#: assets/templates/assets/asset_update.html:33 perms/models.py:74
#: perms/templates/perms/asset_permission_create_update.html:40
#: perms/templates/perms/asset_permission_list.html:67
msgid "Node"
msgstr "节点"
#: assets/templates/assets/asset_create.html:33
#: assets/templates/assets/asset_list.html:69
#: assets/templates/assets/asset_update.html:38
#: assets/templates/assets/asset_create.html:34
#: assets/templates/assets/asset_list.html:75
#: assets/templates/assets/asset_update.html:39
msgid "Label"
msgstr "标签"
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:197
#: assets/templates/assets/asset_detail.html:20 assets/views/asset.py:196
msgid "Asset detail"
msgstr "资产详情"
......@@ -850,50 +844,50 @@ msgstr "刷新"
msgid "Update successfully!"
msgstr "更新成功"
#: assets/templates/assets/asset_list.html:57
#: assets/templates/assets/asset_list.html:114 assets/views/asset.py:96
#: assets/templates/assets/asset_list.html:63
#: assets/templates/assets/asset_list.html:120 assets/views/asset.py:95
msgid "Create asset"
msgstr "创建资产"
#: assets/templates/assets/asset_list.html:61
#: assets/templates/assets/asset_list.html:67
#: users/templates/users/user_list.html:7
msgid "Import"
msgstr "导入"
#: assets/templates/assets/asset_list.html:64
#: assets/templates/assets/asset_list.html:70
#: users/templates/users/user_list.html:10
msgid "Export"
msgstr "导出"
#: assets/templates/assets/asset_list.html:96
#: assets/templates/assets/asset_list.html:102
msgid "Remove from this node"
msgstr "从节点移除"
#: assets/templates/assets/asset_list.html:115
#: assets/templates/assets/asset_list.html:121
msgid "Add asset"
msgstr "添加资产到节点"
#: assets/templates/assets/asset_list.html:117
#: assets/templates/assets/asset_list.html:123
msgid "Add node"
msgstr "新建节点"
#: assets/templates/assets/asset_list.html:118
#: assets/templates/assets/asset_list.html:124
msgid "Rename node"
msgstr "重命名节点"
#: assets/templates/assets/asset_list.html:120
#: assets/templates/assets/asset_list.html:126
msgid "Delete node"
msgstr "删除节点"
#: assets/templates/assets/asset_list.html:195
#: assets/templates/assets/asset_list.html:201
msgid "Create node failed"
msgstr "创建节点失败"
#: assets/templates/assets/asset_list.html:208
#: assets/templates/assets/asset_list.html:214
msgid "Have child node, cancel"
msgstr "存在子节点,不能删除"
#: assets/templates/assets/asset_list.html:477
#: assets/templates/assets/asset_list.html:536
#: assets/templates/assets/system_user_list.html:130
#: users/templates/users/user_detail.html:334
#: users/templates/users/user_detail.html:359
......@@ -902,24 +896,24 @@ msgstr "存在子节点,不能删除"
msgid "Are you sure?"
msgstr "你确认吗?"
#: assets/templates/assets/asset_list.html:478
#: assets/templates/assets/asset_list.html:537
msgid "This will delete the selected assets !!!"
msgstr "删除选择资产"
#: assets/templates/assets/asset_list.html:486
#: assets/templates/assets/asset_list.html:545
msgid "Asset Deleted."
msgstr "已被删除"
#: assets/templates/assets/asset_list.html:487
#: assets/templates/assets/asset_list.html:492
#: assets/templates/assets/asset_list.html:546
#: assets/templates/assets/asset_list.html:551
msgid "Asset Delete"
msgstr "删除"
#: assets/templates/assets/asset_list.html:491
#: assets/templates/assets/asset_list.html:550
msgid "Asset Deleting failed."
msgstr "删除失败"
#: assets/templates/assets/asset_update.html:57
#: assets/templates/assets/asset_update.html:58
msgid "Configuration"
msgstr "配置"
......@@ -978,6 +972,11 @@ msgstr "Uid"
msgid "Add to node"
msgstr "添加到节点"
#: assets/templates/assets/system_user_list.html:16
#: assets/views/system_user.py:45
msgid "Create system user"
msgstr "创建系统用户"
#: assets/templates/assets/system_user_list.html:131
msgid "This will delete the selected System Users !!!"
msgstr "删除选择系统用户"
......@@ -1011,17 +1010,21 @@ msgstr "更新管理用户"
msgid "Admin user detail"
msgstr "管理用户详情"
#: assets/views/asset.py:49 assets/views/asset.py:62 templates/_nav.html:22
#: assets/views/asset.py:49 templates/_nav.html:23
msgid "Asset list"
msgstr "资产列表"
#: assets/views/asset.py:156
#: assets/views/asset.py:61 templates/_nav_user.html:4
msgid "My assets"
msgstr "我的资产"
#: assets/views/asset.py:155
msgid "Bulk update asset"
msgstr "批量更新资产"
#: assets/views/asset.py:173
#: assets/views/asset.py:172
msgid "Update asset"
msgstr "编辑资产"
msgstr "更新资产"
#: assets/views/asset.py:296
msgid "already exists"
......@@ -1033,7 +1036,7 @@ msgstr "标签列表"
#: assets/views/label.py:59
msgid "Update label"
msgstr "编辑标签"
msgstr "更新标签"
#: assets/views/system_user.py:29
msgid "System user list"
......@@ -1192,10 +1195,10 @@ msgstr "密码认证"
#: common/forms.py:156
msgid "Public key auth"
msgstr "钥认证"
msgstr "钥认证"
#: common/forms.py:159 common/templates/common/terminal_setting.html:63
#: terminal/forms.py:21 terminal/models.py:19
#: terminal/forms.py:30 terminal/models.py:20
msgid "Command storage"
msgstr "命令存储"
......@@ -1205,8 +1208,8 @@ msgid ""
"other storage and some terminal using"
msgstr "设置终端命令存储,default是默认用的存储方式"
#: common/forms.py:165 common/templates/common/terminal_setting.html:84
#: terminal/models.py:20
#: common/forms.py:165 common/templates/common/terminal_setting.html:81
#: terminal/forms.py:34 terminal/models.py:21
msgid "Replay storage"
msgstr "录像存储"
......@@ -1263,19 +1266,13 @@ msgid "Test connection"
msgstr "测试连接"
#: common/templates/common/terminal_setting.html:68
#: common/templates/common/terminal_setting.html:89
#: common/templates/common/terminal_setting.html:86
#: users/templates/users/login_log_list.html:50
msgid "Type"
msgstr "类型"
#: common/templates/common/terminal_setting.html:90
#: users/templates/users/reset_password.html:57
#: users/templates/users/user_profile.html:20
msgid "Setting"
msgstr "设置"
#: common/views.py:20 common/views.py:46 common/views.py:72 common/views.py:102
#: templates/_nav.html:66
#: templates/_nav.html:72
msgid "Settings"
msgstr "系统设置"
......@@ -1430,7 +1427,7 @@ 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:124
#: ops/templates/ops/task_history.html:55 terminal/models.py:132
#: terminal/templates/terminal/session_list.html:77
msgid "Date start"
msgstr "开始日期"
......@@ -1542,7 +1539,7 @@ msgstr "任务开始: "
msgid "Ops"
msgstr "作业中心"
#: ops/views.py:37 templates/_nav.html:52
#: ops/views.py:37 templates/_nav.html:58
msgid "Task list"
msgstr "任务列表"
......@@ -1551,8 +1548,9 @@ msgid "Task run history"
msgstr "执行历史"
#: perms/forms.py:22 perms/models.py:16 perms/models.py:75
#: perms/templates/perms/asset_permission_list.html:68 templates/_nav.html:13
#: users/models/user.py:37 users/templates/users/_select_user_modal.html:16
#: perms/templates/perms/asset_permission_list.html:68 templates/_nav.html:14
#: users/models/group.py:25 users/models/user.py:37
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:179
#: users/templates/users/user_list.html:26
msgid "User group"
......@@ -1566,7 +1564,7 @@ msgstr "用户组"
msgid "Date expired"
msgstr "失效日期"
#: perms/models.py:88 templates/_nav.html:32
#: perms/models.py:88 templates/_nav.html:33
msgid "Asset permission"
msgstr "资产授权"
......@@ -1607,10 +1605,6 @@ msgstr "选择资产组"
msgid "Join"
msgstr "加入"
#: perms/templates/perms/asset_permission_create_update.html:17
msgid "Create asset permission "
msgstr "创建资产权限"
#: perms/templates/perms/asset_permission_detail.html:66
msgid "User count"
msgstr "用户数量"
......@@ -1660,7 +1654,7 @@ msgstr "添加用户组"
msgid "Select user groups"
msgstr "选择用户组"
#: perms/views.py:23 perms/views.py:47 perms/views.py:67 templates/_nav.html:29
#: perms/views.py:23 perms/views.py:47 perms/views.py:67 templates/_nav.html:30
msgid "Perms"
msgstr "权限管理"
......@@ -1677,37 +1671,41 @@ msgid "Update asset permission"
msgstr "更新资产授权"
#: templates/_header_bar.html:18
msgid "Help"
msgstr "帮助"
msgid "Supports"
msgstr "商业支持"
#: templates/_header_bar.html:23
msgid "Docs"
msgstr "文档"
#: templates/_header_bar.html:32 templates/_nav_user.html:9
#: templates/_header_bar.html:37 templates/_nav_user.html:9
#: users/templates/users/_user.html:36
#: 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:313
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:316
msgid "Profile"
msgstr "个人信息"
#: templates/_header_bar.html:36
#: templates/_header_bar.html:40
msgid "Admin page"
msgstr "管理页面"
#: templates/_header_bar.html:38
#: templates/_header_bar.html:42
msgid "User page"
msgstr "用户页面"
#: templates/_header_bar.html:41
#: templates/_header_bar.html:45
msgid "Logout"
msgstr "注销登录"
#: templates/_header_bar.html:45 users/templates/users/login.html:42
#: users/templates/users/login.html:61
#: templates/_header_bar.html:49 users/templates/users/login.html:44
#: users/templates/users/login.html:64
msgid "Login"
msgstr "登录"
#: templates/_header_bar.html:58 templates/_nav.html:4
#: templates/_header_bar.html:62 templates/_nav.html:4
msgid "Dashboard"
msgstr "仪表盘"
......@@ -1733,7 +1731,7 @@ msgid ""
" "
msgstr ""
"\n"
" 您的ssh钥没有设置或已失效,请点击 <a href="
" 您的ssh钥没有设置或已失效,请点击 <a href="
"\"%(user_pubkey_update)s\"> 链接 </a> 更新\n"
" "
......@@ -1741,58 +1739,54 @@ msgstr ""
msgid "Close"
msgstr "关闭"
#: 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:200
#: users/views/login.py:249 users/views/user.py:57 users/views/user.py:72
#: users/views/user.py:91 users/views/user.py:147 users/views/user.py:300
#: users/views/user.py:312 users/views/user.py:348 users/views/user.py:370
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95
#: users/views/login.py:209 users/views/login.py:258 users/views/user.py:59
#: users/views/user.py:74 users/views/user.py:93 users/views/user.py:149
#: users/views/user.py:304 users/views/user.py:351 users/views/user.py:373
msgid "Users"
msgstr "用户管理"
#: templates/_nav.html:12 users/views/user.py:58
#: templates/_nav.html:13 users/views/user.py:60
msgid "User list"
msgstr "用户列表"
#: templates/_nav.html:14
#: templates/_nav.html:15
msgid "Login logs"
msgstr "登录日志"
#: templates/_nav.html:38
#: templates/_nav.html:39
msgid "Sessions"
msgstr "会话管理"
#: templates/_nav.html:41
#: templates/_nav.html:42
msgid "Session online"
msgstr "在线会话"
#: templates/_nav.html:42
#: templates/_nav.html:43
msgid "Session offline"
msgstr "历史会话"
#: templates/_nav.html:43
#: templates/_nav.html:44
msgid "Commands"
msgstr "命令记录"
#: templates/_nav.html:44 terminal/templates/terminal/session_list.html:75
#: templates/_nav.html:47 templates/_nav_user.html:14
msgid "Web terminal"
msgstr "Web终端"
#: templates/_nav.html:50 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/session.py:93 terminal/views/session.py:115
#: terminal/views/terminal.py:31 terminal/views/terminal.py:46
#: terminal/views/terminal.py:58
msgid "Terminal"
msgstr "终端管理"
#: templates/_nav.html:49
#: templates/_nav.html:55
msgid "Job Center"
msgstr "作业中心"
#: templates/_nav_user.html:4
msgid "My assets"
msgstr "我的资产"
#: templates/_nav_user.html:14
msgid "Web terminal"
msgstr "Web终端"
#: templates/captcha/image.html:3
msgid "Play CAPTCHA as audio file"
msgstr "语言播放验证码"
......@@ -1819,71 +1813,75 @@ msgstr "输出"
msgid "Session"
msgstr "会话"
#: terminal/forms.py:27
#: terminal/forms.py:44
msgid "Coco ssh listen port"
msgstr "SSH 监听端口"
#: terminal/forms.py:28
#: terminal/forms.py:45
msgid "Coco http/ws listen port"
msgstr "Http/Websocket 监听端口"
#: terminal/models.py:16
#: terminal/models.py:17
msgid "Remote Address"
msgstr "远端地址"
#: terminal/models.py:17
#: terminal/models.py:18
msgid "SSH Port"
msgstr "SSH端口"
#: terminal/models.py:18
#: terminal/models.py:19
msgid "HTTP Port"
msgstr "HTTP端口"
#: terminal/models.py:91
#: terminal/models.py:98
msgid "Session Online"
msgstr "在线会话"
#: terminal/models.py:92
#: terminal/models.py:99
msgid "CPU Usage"
msgstr "CPU使用"
#: terminal/models.py:93
#: terminal/models.py:100
msgid "Memory Used"
msgstr "内存使用"
#: terminal/models.py:94
#: terminal/models.py:101
msgid "Connections"
msgstr "连接数"
#: terminal/models.py:95
#: terminal/models.py:102
msgid "Threads"
msgstr "线程数"
#: terminal/models.py:96
#: terminal/models.py:103
msgid "Boot Time"
msgstr "运行时间"
#: terminal/models.py:119 terminal/templates/terminal/session_list.html:74
#: terminal/models.py:126 terminal/templates/terminal/session_list.html:74
#: terminal/templates/terminal/terminal_detail.html:47
msgid "Remote addr"
msgstr "远端地址"
#: terminal/models.py:121 terminal/templates/terminal/session_list.html:100
#: terminal/models.py:128 terminal/templates/terminal/session_list.html:102
msgid "Replay"
msgstr "回放"
#: terminal/models.py:122 terminal/templates/terminal/command_list.html:55
#: terminal/models.py:129 terminal/templates/terminal/command_list.html:55
#: terminal/templates/terminal/command_list.html:71
#: terminal/templates/terminal/session_detail.html:48
#: terminal/templates/terminal/session_list.html:76
msgid "Command"
msgstr "命令"
#: terminal/models.py:125
#: terminal/models.py:131
msgid "Date last active"
msgstr "最后活跃日期"
#: terminal/models.py:133
msgid "Date end"
msgstr "结束日期"
#: terminal/models.py:142
#: terminal/models.py:150
msgid "Args"
msgstr "参数"
......@@ -1892,7 +1890,7 @@ msgid "Goto"
msgstr "转到"
#: terminal/templates/terminal/session_detail.html:17
#: terminal/views/session.py:115
#: terminal/views/session.py:116
msgid "Session detail"
msgstr "会话详情"
......@@ -1922,19 +1920,23 @@ msgstr "监控"
msgid "Terminate session"
msgstr "终止会话"
#: terminal/templates/terminal/session_list.html:78
#: terminal/templates/terminal/session_list.html:79
msgid "Duration"
msgstr "时长"
#: terminal/templates/terminal/session_list.html:102
#: terminal/templates/terminal/session_list.html:104
msgid "Monitor"
msgstr "监控"
#: terminal/templates/terminal/session_list.html:103
#: terminal/templates/terminal/session_list.html:105
msgid "Terminate"
msgstr "终断"
#: terminal/templates/terminal/session_list.html:119
#: terminal/templates/terminal/session_list.html:116
msgid "Terminate selected"
msgstr "终断所选"
#: terminal/templates/terminal/session_list.html:136
msgid "Terminate task send, waiting ..."
msgstr "终断任务已发送,请等待"
......@@ -1981,7 +1983,7 @@ msgstr "信息"
msgid "Session online list"
msgstr "在线会话"
#: terminal/views/session.py:93
#: terminal/views/session.py:94
msgid "Session offline list"
msgstr "离线会话"
......@@ -2105,31 +2107,31 @@ msgstr "ssh密钥不合法"
msgid "Select users"
msgstr "选择用户"
#: users/models/authentication.py:35
#: users/models/authentication.py:36
msgid "Private Token"
msgstr "ssh密钥"
#: users/models/authentication.py:45
#: users/models/authentication.py:46
msgid "Login type"
msgstr "登录方式"
#: users/models/authentication.py:46
#: users/models/authentication.py:47
msgid "Login ip"
msgstr "登录IP"
#: users/models/authentication.py:47
#: users/models/authentication.py:48
msgid "Login city"
msgstr "登录城市"
#: users/models/authentication.py:48
#: users/models/authentication.py:49
msgid "User agent"
msgstr "Agent"
#: users/models/authentication.py:49
#: users/models/authentication.py:50
msgid "Date login"
msgstr "登录日期"
#: users/models/user.py:29 users/models/user.py:257
#: users/models/user.py:29 users/models/user.py:262
msgid "Administrator"
msgstr "管理员"
......@@ -2168,7 +2170,7 @@ msgstr "二次验证"
msgid "Public key"
msgstr "ssh公钥"
#: users/models/user.py:260
#: users/models/user.py:265
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
......@@ -2239,7 +2241,7 @@ msgid " for more information"
msgstr "获取更多信息"
#: users/templates/users/forgot_password.html:26
#: users/templates/users/login.html:64
#: users/templates/users/login.html:73
msgid "Forgot password"
msgstr "忘记密码"
......@@ -2247,7 +2249,7 @@ msgstr "忘记密码"
msgid "Input your email, that will send a mail to your"
msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
#: users/templates/users/login.html:47
#: users/templates/users/login.html:50
msgid "Captcha invalid"
msgstr "验证码错误"
......@@ -2269,8 +2271,13 @@ msgstr "重置密码"
msgid "Password again"
msgstr "再次输入密码"
#: users/templates/users/reset_password.html:57
#: users/templates/users/user_profile.html:20
msgid "Setting"
msgstr "设置"
#: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:16 users/views/user.py:72
#: users/templates/users/user_list.html:16 users/views/user.py:74
msgid "Create user"
msgstr "创建用户"
......@@ -2279,7 +2286,7 @@ msgid "Reset link will be generated and sent to the user. "
msgstr "生成重置密码连接,通过邮件发送给用户"
#: users/templates/users/user_detail.html:19
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:148
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:150
msgid "User detail"
msgstr "用户详情"
......@@ -2320,7 +2327,7 @@ msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱
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:350
#: users/templates/users/user_profile.html:140
......@@ -2329,7 +2336,7 @@ msgstr "重置SSH密钥"
#: users/templates/users/user_detail.html:360
msgid "This will reset the user public key and send a reset mail"
msgstr "将会失效用户当前钥,并发送重置邮件到用户邮箱"
msgstr "将会失效用户当前钥,并发送重置邮件到用户邮箱"
#: users/templates/users/user_detail.html:377
#: users/templates/users/user_profile.html:166
......@@ -2351,7 +2358,7 @@ msgstr "取消"
#: users/templates/users/user_group_granted_asset.html:18
#: users/views/group.py:80
msgid "User group detail"
msgstr "资产组详情"
msgstr "用户组详情"
#: users/templates/users/user_group_detail.html:86
msgid "Add user"
......@@ -2399,8 +2406,8 @@ msgstr "用户删除失败"
msgid "OTP"
msgstr ""
#: users/templates/users/user_profile.html:100 users/views/user.py:177
#: users/views/user.py:229
#: users/templates/users/user_profile.html:100 users/views/user.py:179
#: users/views/user.py:233
msgid "User groups"
msgstr "用户组"
......@@ -2420,9 +2427,9 @@ msgstr "指纹"
msgid "Update public key"
msgstr "更新密钥"
#: users/templates/users/user_update.html:4 users/views/user.py:91
#: users/templates/users/user_update.html:4 users/views/user.py:93
msgid "Update user"
msgstr "编辑用户"
msgstr "更新用户"
#: users/utils.py:35
msgid "Create account successfully"
......@@ -2552,7 +2559,7 @@ msgstr "禁用或失效"
#: users/utils.py:154
msgid "Password or SSH public key invalid"
msgstr "密码或钥不合法"
msgstr "密码或钥不合法"
#: users/views/group.py:29
msgid "User group list"
......@@ -2560,78 +2567,84 @@ msgstr "用户组列表"
#: users/views/group.py:63
msgid "Update user group"
msgstr "编辑用户组"
msgstr "更新用户组"
#: users/views/group.py:96
msgid "User group granted asset"
msgstr "用户组授权资产"
#: users/views/login.py:56
#: users/views/login.py:57
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: users/views/login.py:90
#: users/views/login.py:99
msgid "Logout success"
msgstr "退出登录成功"
#: users/views/login.py:91
#: users/views/login.py:100
msgid "Logout success, return login page"
msgstr "退出登录成功,返回到登录页面"
#: users/views/login.py:107
#: users/views/login.py:116
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
#: users/views/login.py:120
#: users/views/login.py:129
msgid "Send reset password message"
msgstr "发送重置密码邮件"
#: users/views/login.py:121
#: users/views/login.py:130
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
#: users/views/login.py:135
#: users/views/login.py:144
msgid "Reset password success"
msgstr "重置密码成功"
#: users/views/login.py:136
#: users/views/login.py:145
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
#: users/views/login.py:153 users/views/login.py:166
#: users/views/login.py:162 users/views/login.py:175
msgid "Token invalid or expired"
msgstr "Token错误或失效"
#: users/views/login.py:162
#: users/views/login.py:171
msgid "Password not same"
msgstr "密码不一致"
#: users/views/login.py:200
#: users/views/login.py:209
msgid "First login"
msgstr "首次登陆"
#: users/views/login.py:250
#: users/views/login.py:259
msgid "Login log list"
msgstr "登录日志"
#: users/views/user.py:101
#: users/views/user.py:103
msgid "Bulk update user success"
msgstr "批量更新用户成功"
#: users/views/user.py:206
#: users/views/user.py:208
msgid "Invalid file."
msgstr "文件不合法"
#: users/views/user.py:301
#: users/views/user.py:305
msgid "User granted assets"
msgstr "用户授权资产"
#: users/views/user.py:331
#: users/views/user.py:334
msgid "Profile setting"
msgstr "个人信息设置"
#: users/views/user.py:349
#: users/views/user.py:352
msgid "Password update"
msgstr "密码更新"
#: users/views/user.py:371
#: users/views/user.py:374
msgid "Public key update"
msgstr "钥更新"
msgstr "钥更新"
#~ msgid "Create asset permission "
#~ msgstr "创建资产权限"
......@@ -397,6 +397,6 @@ BOOTSTRAP3 = {
}
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION or 3600
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE or 25
DEFAULT_EXPIRED_YEARS = 70
USER_GUIDE_URL = ""
......@@ -4,16 +4,16 @@ from __future__ import unicode_literals
from django.conf.urls import url, include
from django.conf import settings
from django.conf.urls.static import static
from django.views.static import serve as static_serve
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer
from .views import IndexView
from .views import IndexView, LunaView
schema_view = get_schema_view(title='Users API', renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer])
urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'),
url(r'^luna/', LunaView.as_view(), name='luna-error'),
url(r'^users/', include('users.urls.views_urls', namespace='users')),
url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
......
from django.views.generic import TemplateView
import datetime
from django.http import HttpResponse
from django.views.generic import TemplateView, View
from django.utils import timezone
from django.db.models import Count
from django.contrib.auth.mixins import LoginRequiredMixin
......@@ -45,15 +48,22 @@ class IndexView(LoginRequiredMixin, TemplateView):
return self.session_week.values('user').distinct().count()
def get_week_login_asset_count(self):
return self.session_week.values('asset').distinct().count()
return self.session_week.count()
# return self.session_week.values('asset').distinct().count()
def get_month_day_metrics(self):
month_str = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
return month_str
def get_month_login_metrics(self):
return [self.session_month.filter(date_start__date=d).count()
for d in self.session_month_dates]
data = []
time_min = datetime.datetime.min.time()
time_max = datetime.datetime.max.time()
for d in self.session_month_dates:
ds = datetime.datetime.combine(d, time_min).replace(tzinfo=timezone.get_current_timezone())
de = datetime.datetime.combine(d, time_max).replace(tzinfo=timezone.get_current_timezone())
data.append(self.session_month.filter(date_start__range=(ds, de)).count())
return data
def get_month_active_user_metrics(self):
if self.session_month_dates_archive:
......@@ -119,10 +129,18 @@ class IndexView(LoginRequiredMixin, TemplateView):
self.session_week = Session.objects.filter(date_start__gt=week_ago)
self.session_month = Session.objects.filter(date_start__gt=month_ago)
self.session_month_dates = self.session_month.dates('date_start', 'day')
self.session_month_dates_archive = [
self.session_month.filter(date_start__date=d)
for d in self.session_month_dates
]
self.session_month_dates_archive = []
time_min = datetime.datetime.min.time()
time_max = datetime.datetime.max.time()
for d in self.session_month_dates:
ds = datetime.datetime.combine(d, time_min).replace(
tzinfo=timezone.get_current_timezone())
de = datetime.datetime.combine(d, time_max).replace(
tzinfo=timezone.get_current_timezone())
self.session_month_dates_archive.append(
self.session_month.filter(date_start__range=(ds, de)))
context = {
'assets_count': self.get_asset_count(),
......@@ -149,3 +167,12 @@ class IndexView(LoginRequiredMixin, TemplateView):
kwargs.update(context)
return super(IndexView, self).get_context_data(**kwargs)
class LunaView(View):
def get(self, request):
msg = """
Luna是单独部署的一个程序,你需要部署luna,coco,配置nginx做url分发,
如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运
"""
return HttpResponse(msg)
\ No newline at end of file
......@@ -12,7 +12,7 @@ class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = NodePermission
fields = [
'node', 'user_group', 'system_user',
'id', 'node', 'user_group', 'system_user',
'is_active', 'date_expired'
]
......
......@@ -14,7 +14,7 @@
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="ibox-title">
<h5>{% trans 'Create asset permission ' %}</h5>
<h5>{{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
......
......@@ -186,7 +186,7 @@ function initTree() {
{#if (value["key"] === "0") {#}
value["open"] = true;
{# }#}
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')'
value["name"] = value["value"]
});
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
......
......@@ -427,3 +427,9 @@ div.dataTables_wrapper div.dataTables_filter {
text-align: center;
padding: 5px 0;
}
.profile-dropdown li a {
font-size: 12px !important;
}
......@@ -3299,7 +3299,7 @@ body.tour-open .animated {
border-bottom: 1px solid #e7eaec;
}
body {
font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "open sans", "Helvetica Neue", "微软雅黑", Helvetica, Arial, sans-serif;
background-color: #2f4050;
font-size: 13px;
color: #676a6c;
......
apps/static/img/logo-text.png

12.4 KB | W: | H:

apps/static/img/logo-text.png

17.7 KB | W: | H:

apps/static/img/logo-text.png
apps/static/img/logo-text.png
apps/static/img/logo-text.png
apps/static/img/logo-text.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -14,8 +14,13 @@
{# <span class="m-r-sm text-muted welcome-message">{% trans 'Welcome to use Jumpserver system' %}</span>#}
{# </li>#}
<li class="dropdown">
<a class="dropdown-toggle count-info" data-toggle="dropdown" href="#">
<span class="m-r-sm text-muted welcome-message">{% trans 'Help' %}</span>
<a class="count-info" href="https://market.aliyun.com/products/53690006/cmgj026011.html?spm=5176.730005.0.0.cY2io1" target="_blank">
<span class="m-r-sm text-muted welcome-message">{% trans 'Supports' %}</span>
</a>
</li>
<li class="dropdown">
<a class="count-info" href="http://jumpserver.readthedocs.io/" target="_blank">
<span class="m-r-sm text-muted welcome-message">{% trans 'Docs' %}</span>
</a>
</li>
<li class="dropdown">
......@@ -28,9 +33,8 @@
</span>
</span>
</a>
<ul class="dropdown-menu animated fadeInRight m-t-xs">
<li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
<li class="divider"></li>
<ul class="dropdown-menu animated fadeInRight m-t-xs profile-dropdown">
<li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
{% if request.user.is_superuser %}
{% 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>
......@@ -57,11 +61,11 @@
<li>
<a href="">{% trans 'Dashboard' %}</a>
</li>
<li>
{% if app %}
<li>
<a>{{ app }}</a>
{% endif %}
</li>
{% endif %}
{% if action %}
<li class="active">
<strong>{{ action }}</strong>
......
......@@ -42,6 +42,11 @@
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
<li>
<a href="{% url 'terminal:web-terminal' %}" target="_blank">
<span class="nav-label">{% trans 'Web terminal' %}</span>
</a>
</li>
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
</ul>
</li>
......
......@@ -3,7 +3,7 @@
<li class="nav-header">
<div class="dropdown profile-element">
<div href="http://www.jumpserver.org" target="_blank">
<img alt="image" height="55" src="/static/img/logo-text.png" style="margin-left: 10px"/>
<img alt="logo" height="55" width="185" src="/static/img/logo-text.png" style="margin-left: 20px"/>
</div>
</div>
<div class="clearfix"></div>
......
......@@ -57,7 +57,7 @@
<div class="row">
<div class="col-sm-2 border-bottom white-bg dashboard-header" style="margin-left:15px;height: 346px">
<h2>活跃用户TOP5</h2>
<small>过去一周共有<span class="text-info">{{ user_visit_count_weekly }}</span>位用户登录<span class="text-success">{{ asset_visit_count_weekly }}</span>服务器.</small>
<small>过去一周共有<span class="text-info">{{ user_visit_count_weekly }}</span>位用户登录<span class="text-success">{{ asset_visit_count_weekly }}</span>资产.</small>
<ul class="list-group clear-list m-t">
{% for data in user_visit_count_top_five %}
<li class="list-group-item fist-item">
......
......@@ -5,14 +5,15 @@ import logging
import os
import uuid
from rest_framework import viewsets, serializers
from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny
from django.core.cache import cache
from django.shortcuts import get_object_or_404, redirect
from django.utils import timezone
from django.core.files.storage import default_storage
from django.http import HttpResponseNotFound
from rest_framework import viewsets, serializers
from rest_framework.views import APIView, Response
from rest_framework.permissions import AllowAny
from rest_framework_bulk import BulkModelViewSet
from common.utils import get_object_or_none
from .models import Terminal, Status, Session, Task
......@@ -179,12 +180,29 @@ class SessionViewSet(viewsets.ModelViewSet):
return self.queryset
class TaskViewSet(viewsets.ModelViewSet):
class TaskViewSet(BulkModelViewSet):
queryset = Task.objects.all()
serializer_class = TaskSerializer
permission_classes = (IsSuperUserOrAppUser,)
class KillSessionAPI(APIView):
permission_classes = (IsSuperUserOrAppUser,)
model = Task
def post(self, request, *args, **kwargs):
validated_session = []
for session_id in request.data:
session = get_object_or_none(Session, id=session_id)
if session and not session.is_finished:
validated_session.append(session_id)
self.model.objects.create(
name="kill_session", args=session.id,
terminal=session.terminal,
)
return Response({"ok": validated_session})
class CommandViewSet(viewsets.ViewSet):
"""接受app发送来的command log, 格式如下
{
......
......@@ -129,7 +129,7 @@ class Session(models.Model):
has_command = models.BooleanField(default=False, verbose_name=_("Command"))
terminal = models.ForeignKey(Terminal, null=True, on_delete=models.CASCADE)
date_last_active = models.DateTimeField(verbose_name=_("Date last active"), default=timezone.now)
date_start = models.DateTimeField(verbose_name=_("Date start"))
date_start = models.DateTimeField(verbose_name=_("Date start"), db_index=True)
date_end = models.DateTimeField(verbose_name=_("Date end"), null=True)
class Meta:
......
......@@ -3,7 +3,11 @@
from django.utils import timezone
from rest_framework import serializers
from rest_framework_bulk.serializers import BulkListSerializer
from common.mixins import BulkSerializerMixin
from common.utils import get_object_or_none
from .models import Terminal, Status, Session, Task
from .backends import get_multi_command_store
......@@ -47,6 +51,7 @@ class SessionSerializer(serializers.ModelSerializer):
class Meta:
model = Session
list_serializer_class = BulkListSerializer
fields = '__all__'
def get_command_amount(self, obj):
......@@ -60,11 +65,12 @@ class StatusSerializer(serializers.ModelSerializer):
model = Status
class TaskSerializer(serializers.ModelSerializer):
class TaskSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Task
list_serializer_class = BulkListSerializer
class ReplaySerializer(serializers.Serializer):
......
......@@ -8,7 +8,7 @@ from django.utils import timezone
from common.celery import register_as_period_task, after_app_ready_start, \
after_app_shutdown_clean
from .models import Status
from .models import Status, Session
CACHE_REFRESH_INTERVAL = 10
......@@ -20,11 +20,17 @@ RUNNING = False
@after_app_ready_start
@after_app_shutdown_clean
def delete_terminal_status_period():
yesterday = timezone.now() - datetime.timedelta(days=1)
yesterday = timezone.now() - datetime.timedelta(days=3)
Status.objects.filter(date_created__lt=yesterday).delete()
@shared_task
@register_as_period_task(interval=3600)
@after_app_ready_start
@after_app_shutdown_clean
def clean_orphan_session():
active_sessions = Session.objects.filter(is_finished=False)
for session in active_sessions:
if not session.terminal.is_active:
session.is_finished = True
session.save()
......@@ -109,6 +109,21 @@
{% endfor %}
{% endblock %}
{% block content_bottom_left %}
<div id="actions" {% if type != "online" %} style="display: none" {% endif %}>
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="terminate">{% trans 'Terminate selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
......@@ -119,7 +134,7 @@
}, 1000)
}
var success_message = '{% trans "Terminate task send, waiting ..." %}';
var the_url = "{% url 'api-terminal:tasks-list' %}";
var the_url = "{% url 'api-terminal:kill-session' %}";
APIUpdateAttr({
url: the_url,
method: 'POST',
......@@ -150,14 +165,31 @@
}).on('click', '.btn-term', function () {
var $this = $(this);
var session_id = $this.attr('value');
var terminal_id = $this.attr('terminal');
var data = {
name: "kill_session",
args: session_id,
terminal: terminal_id
};
var data = [
session_id
];
terminateSession(data)
})
}).on('click', '#btn_bulk_update', function () {
var action = $('#slct_bulk_update').val();
var id_list = [];
$(".cbx-term:checked").each(function (index, data) {
id_list.push($(data).attr("value"))
});
if (id_list.length === 0) {
return false;
}
function doTerminate() {
terminateSession(id_list)
}
switch(action) {
case 'terminate':
doTerminate();
break;
default:
break;
}
});
</script>
{% endblock %}
......@@ -22,6 +22,7 @@ urlpatterns = [
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
name='session-replay'),
url(r'^v1/tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'),
url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(), name='terminal-access-key'),
url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'),
]
......
......@@ -6,7 +6,7 @@ from django.conf import settings
from django.utils import timezone
from django.utils.translation import ugettext as _
from common.mixins import DatetimeSearchMixin
from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin
from ..models import Command
from .. import utils
from ..backends import get_multi_command_store
......@@ -15,7 +15,7 @@ __all__ = ['CommandListView']
common_storage = get_multi_command_store()
class CommandListView(DatetimeSearchMixin, ListView):
class CommandListView(DatetimeSearchMixin, AdminUserRequiredMixin, ListView):
model = Command
template_name = "terminal/command_list.html"
context_object_name = 'command_list'
......
......@@ -74,6 +74,7 @@ class SessionOnlineListView(SessionListView):
context = {
'app': _('Terminal'),
'action': _('Session online list'),
'type': 'online',
'now': timezone.now(),
}
kwargs.update(context)
......@@ -97,7 +98,7 @@ class SessionOfflineListView(SessionListView):
return super().get_context_data(**kwargs)
class SessionDetailView(SingleObjectMixin, ListView):
class SessionDetailView(SingleObjectMixin, AdminUserRequiredMixin, ListView):
template_name = 'terminal/session_detail.html'
model = Session
object = None
......
......@@ -145,7 +145,8 @@ class UserAuthApi(APIView):
if not login_ip:
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
if x_forwarded_for:
if x_forwarded_for and x_forwarded_for[0]:
login_ip = x_forwarded_for[0]
else:
login_ip = request.META.get("REMOTE_ADDR")
......
......@@ -16,7 +16,8 @@ class AccessKey(models.Model):
default=uuid.uuid4, editable=False)
secret = models.UUIDField(verbose_name='AccessKeySecret',
default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, verbose_name='User', on_delete=models.CASCADE, related_name='access_key')
user = models.ForeignKey(User, verbose_name='User',
on_delete=models.CASCADE, related_name='access_key')
def get_id(self):
return str(self.id)
......
......@@ -22,6 +22,7 @@ class UserGroup(NoDeleteModelMixin):
class Meta:
ordering = ['name']
verbose_name = _("User group")
@classmethod
def initial(cls):
......
......@@ -251,6 +251,7 @@ class User(AbstractUser):
class Meta:
ordering = ['username']
verbose_name = _("User")
#: Use this method initial user
@classmethod
......
......@@ -22,24 +22,27 @@
<div class="loginColumns animated fadeInDown">
<div class="row">
<div class="col-md-6">
<h2 class="font-bold">欢迎使用Jumpserver开源跳板</h2>
<h2 class="font-bold">欢迎使用Jumpserver开源堡垒</h2>
<p>
符合4A规范的专业运维审计系统:拥有跳板机的所有功能,认证,授权,审计,文件上传;
全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
</p>
<p>
极致的用户使用体验:拥有时尚外观是区别与以往版本和其他软件的铭牌,高雅的气质让你爱不释手;
使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
</p>
<p>
混合云环境下的堡垒机:怎么能容忍传统堡垒机的繁琐步骤,Jumpserver让你极致省力;
采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发访问限制。
</p>
<p>
<small>改变世界,从一点点开始。</small>
改变世界,从一点点开始。
</p>
</div>
<div class="col-md-6">
<div class="ibox-content">
<div><img src="{% static 'img/logo.png' %}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">{% trans 'Login' %}</span></div>
<div>
<img src="{% static 'img/logo.png' %}" width="60" height="60">
<span class="font-bold text-center" style="font-size: 24px; font-family: inherit; margin-left: 20px">{% trans 'Login' %}</span>
</div>
<form class="m-t" role="form" method="post" action="">
{% csrf_token %}
{% if form.errors %}
......@@ -60,12 +63,16 @@
</div>
<button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Login' %}</button>
{% if demo_mode %}
<p class="text-muted font-bold" style="color: red">
Demo账号: admin 密码: admin
</p>
{% endif %}
<a href="{% url 'users:forgot-password' %}">
<small>{% trans 'Forgot password' %}?</small>
</a>
<p class="text-muted text-center">
</p>
</form>
<p class="m-t">
</p>
......
......@@ -92,8 +92,8 @@ class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
context = {
'app': 'User',
'action': 'User group granted asset',
'app': _('Users'),
'action': _('User group granted asset'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
import os
from django import forms
from django.shortcuts import render
from django.contrib.auth import login as auth_login, logout as auth_logout
......@@ -56,6 +57,7 @@ class UserLoginView(FormView):
return HttpResponse(_("Please enable cookies and try again."))
auth_login(self.request, form.get_user())
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
if x_forwarded_for and x_forwarded_for[0]:
login_ip = x_forwarded_for[0]
else:
......@@ -75,6 +77,13 @@ class UserLoginView(FormView):
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name, reverse('index')))
def get_context_data(self, **kwargs):
context = {
'demo_mode': os.environ.get("DEMO_MODE"),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
@method_decorator(never_cache, name='dispatch')
class UserLogoutView(TemplateView):
......@@ -237,7 +246,7 @@ class LoginLogListView(DatetimeSearchMixin, ListView):
if self.user:
queryset = queryset.filter(username=self.user)
if self.keyword:
queryset = self.queryset.filter(
queryset = queryset.filter(
Q(ip__contains=self.keyword) |
Q(city__contains=self.keyword) |
Q(username__contains=self.keyword)
......
......@@ -313,7 +313,6 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
context = {
'app': _('Users'),
'action': _('Profile'),
}
kwargs.update(context)
......
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = Jumpserver
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
资产管理模块
=============
\ No newline at end of file
管理文档
=========
这里介绍管理员功能。
.. toctree::
:maxdepth: 1
admin_user
admin_asset
用户管理
========
这里介绍用户管理模块的功能。
点击页面左侧“用户列表”菜单下的“用户列表,进入用户列表页面。
.. contents:: Topics
.. _create_user:
创建用户
````````
点击页面左上角“创建用户”按钮,进入创建用户页面,填写账户,角色安全,个人等信息,点击“提交”按钮,用户创建完成。
.. _update_user:
更新用户
````````
点击页面右边的“更新”按钮,进入编辑用户页面,编辑用户信息,点击“提交”按钮,更新用户完成。
.. _delete_user:
删除用户
````````
点击页面右边的“删除”按钮,弹出是否删除确认框,点击“确定”按钮,删除用户完成。
.. _export_user:
导出用户
````````
选中用户,点击右上角的“导出”按钮,导出用户完成。
.. _inport_user:
导入用户
````````
点击右上角的“导入”按钮,弹出导入对话框,选择要导入的CSV格式文件,点击“确认”按钮,导入用户完成。
.. _batch_operation:
批量操作
````````
选中用户,选择页面左下角的批量操作选项,点击”提交“按钮,批量操作完成。
\ No newline at end of file
REST API规范约定
----------------
这里仅考虑REST API的基本情况。参考
`RESTful API 设计指南`_
`github api文档`_
协议
~~~~
API与用户的通信协议,总是使用HTTPs协议。
域名
~~~~
这版api相对简单, 没有前后端分离, 没有独立app, 所以放在主域名下
::
https://example.org/api/
版本
~~~~
将API的版本号放入URL中, 由于一个项目多个app所以Jumpserver使用以下风格,
将版本号放到app后面
::
https://example.com/api/:app:/:version:/:resource:
https://example.com/api/assets/v1.0/assets [GET, POST]
https://example.com/api/assets/v1.0/assets/1 [GET, PUT, DELETE]
路径
~~~~
路径又称“终点”(endpoint),表示API的具体网址。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的“集合”(collection),所以API中的名词也应该使用复数。
举例来说 cmdb中的assets列表, idc列表
::
https://example.com/api/:app:/:version:/:resource:
https://example.com/api/assets/v1.0/assets [GET, POST]
https://example.com/api/assets/v1.0/assets/1 [GET, PUT, DELETE]
https://example.com/api/assets/v1.0/idcs [GET, POST]
一般性的增删查改(CRUD)API,完全使用HTTP
method加上url提供的语义,url中的可变部分(比如上面提到的)
一般用来传递该API操作的核心实体对象的唯一ID,如果有更多的参数需要提供,GET方法请使用url
parameter
(例如:“?client_id=xxxxx&app_id=xxxxxx”),PUT/POST/DELETE方法请使用请求体传递参数。
HTTP Method
~~~~~~~~~~~
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面五个(括号里是对应的SQL命令)。
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源, 幂等
- PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
- DELETE(DELETE):从服务器删除资源。
.. _RESTful API 设计指南: http://www.ruanyifeng.com/blog/2014/05/restful_api.html
.. _github api文档: https://developer.github.com/v3/
过滤信息
~~~~~~~~
常见参数约定
::
?keyword=localhost 模糊搜索
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sort=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?asset_id=1:指定筛选条件
状态码
~~~~~~
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
- 200 OK -
[GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
- 400 INVALID REQUEST -
[POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [*]
表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND -
[*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable -
[GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH]
当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR -
[*]:服务器发生错误,用户将无法判断发出的请求是否成功。
错误处理
~~~~~~~~
如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
::
{
error: "Invalid API key"
}
返回结果
~~~~~~~~
针对不同操作,服务器向用户返回的结果应该符合以下规范。
::
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
Hypermedia API
~~~~~~~~~~~~~~
RESTful
API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。
::
{"link": {
"rel": "collection https://www.example.com/zoos",
"href": "https://api.example.com/zoos",
"title": "List of zoos",
"type": "application/vnd.yourformat+json"
}}
上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。
rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),
href表示API的路径,title表示API的标题,type表示返回类型。 Hypermedia
API的设计被称为HATEOAS。 Github的API就是这种设计.
其它
~~~~
(1)API的身份认证应该使用OAuth 2.0框架。
(2)服务器返回的数据格式,应该尽量使用JSON
\ No newline at end of file
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/stable/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import sphinx_rtd_theme
# -- Project information -----------------------------------------------------
project = 'jumpserver'
copyright = '北京堆栈科技有限公司 © 2014-2018'
author = 'Jumpserver team'
# The short X.Y version
version = ''
# The full version, including alpha/beta/rc tags
release = '0.5.0'
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.viewcode',
'sphinx.ext.githubpages',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'zh_CN'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
html_show_sourcelink = False
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
# html_theme = 'alabaster'
html_theme = "sphinx_rtd_theme"
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'logo_only': True,
'display_version': False
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'Jumpserver 文档'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'jumpserver.tex', 'jumpserver Documentation',
'Jumpserver team', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'jumpserver', 'jumpserver Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'jumpserver', 'jumpserver Documentation',
author, 'jumpserver', 'One line description of project.',
'Miscellaneous'),
]
# -- Extension configuration -------------------------------------------------
html_logo = '_static/img/logo-text.png'
联系方式
+++++++++++++++++++++++++
QQ群
~~~~~~~~
群1: 390139816
群2: 399218702
群3: 552054376
Github
~~~~~~~~
https://github.com/jumpserver/jumpserver.git
官网
~~~~~~~~
http://www.jumpserver.org
Demo
~~~~~~~~
http://demo.jumpserver.org:8080
邮件
~~~~~~~~
ibuler#fit2cloud.com (#替换为@)
\ No newline at end of file
贡献者
++++++++++++++++++++++++
感谢一下朋友为Jumpserver做出的贡献,世界因你们而不同,排名不分先后
- **小彧 <李磊>** Django资深开发者,为用户模块贡献了很多代码
- **sofia <周小侠>** 资深前端工程师, 前端代码贡献者
- **liuz <刘正> 全栈工程师** 编写了Web terminal大部分代码
- **jiaxiangkong <陈尚委>** Jumpserver测试运营
- **halcyon <王墉>** DevOps 资深开发者, 0.3.2 核心开发者之一
- **yumaojun03 <喻茂峻>** DevOps 资深开发者,擅长Python, Go以及PAAS平台开发
- **kelianchun <柯连春>** DevOps 资产开发者,fix了很多bug
\ No newline at end of file
开发文档
======================================
.. toctree::
:maxdepth: 1
:caption: 开发文档
api_style_guide
python_style_guide
project_structure
FAQ
+++++++++++++++++++++
\ No newline at end of file
.. jumpserver documentation master file, created by
sphinx-quickstart on Mon Feb 26 23:28:27 2018.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Jumpserver 文档
======================================
.. toctree::
:maxdepth: 2
:caption: 文档:
intro
installation
admin_guide
user_guide
development
contributor
contact
faq
索引
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
安装文档
++++++++++++++++++++++++
.. toctree::
:maxdepth: 1
quickstart
step_by_step
upgrade
简介
============
Jumpserver是混合云下更好用的堡垒机, 分布式架构设计无限扩展,轻松对接混合云资产,支持使用云存储(AWS S3, ES等)存储录像、命令
Jumpserver颠覆传统堡垒机, 无主机和并发数量限制,支持水平扩容,FIT2CLOUD提供完备的商业服务支持,用户无后顾之忧
Jumpserver拥有极致的用户体验, 极致UI体验,容器化的部署方式,部署过程方便快捷,可持续升级
组件说明
++++++++++++++++++++++++
Jumpserver
```````````
现指Jumpserver管理后台,是核心组件(Core), 使用 Django Class Based View 风格开发,支持Restful API。
`Github <https://github.com/jumpserver/jumpserver.git>`_
Coco
````````
实现了SSH Server 和 Web Terminal Server的组件,提供ssh和websocket接口, 使用 Paramiko 和 Flask 开发。
`Github <https://github.com/jumpserver/coco.git>`__
Luna
````````
现在是Web Terminal前端,计划前端页面都由该项目提供,Jumpserver只提供API,不再负责后台渲染html等。
`Github <https://github.com/jumpserver/luna.git>`__
Guacamole
```````````
Apache 跳板机项目,Jumpserver使用其组件实现RDP功能,Jumpserver并没有修改其代码而是添加了额外的插件,支持Jumpserver调用
Jumpserver-python-sdk
```````````````````````
Jumpserver API Python SDK,Coco目前使用该SDK与Jumpserver API交互
`Github <https://github.com/jumpserver/jumpserver-python-sdk.git>`__
组件架构图
++++++++++++++++++++++++
.. image:: _static/img/structure.png
:alt: 组件架构图
项目骨架
--------
说明如下:
::
.
├── config-example.py // 配置文件样例
├── docs // 所有doc文件放到该目录
│ └── README.md
├── LICENSE
├── README.md
├── install // 安装说明
├── logs // 日志目录
├── apps // 管理后台目录,也是各app所在目录
│ └── assets // app目录
│ │ ├── admin.py
│ │ ├── apps.py // 新版本django app设置文件
│ │ ├── api.py // api文件
│ │ ├── __init__.py // 对外暴露的接口,放到该文件中,方便别的app引用
│ │ ├── migrations // models Migrations版本控制目录
│ │ │ └── __init__.py
│ │ ├── models.py // 数据模型目录
│ │ ├── static // app下静态资源目录,如果需要
│ │ │ └── assets // 多一层目录,防止资源重名
│ │ │ └── some_image.png
│ │ ├── templates // app下模板目录
│ │ │ └── assets // 多一层目录,防止资源重名
│ │ │ └── asset_list.html
│ │ ├── templatetags // 模板标签目录
│ │ ├── tests.py // 测试用例文件
│ │ ├── urls.py // urlconf文件
│ │ ├── utils.py // 将views和api可复用的代码放在这里, api和views只是请求和返回不同
│ │ └── views.py // views文件
│ ├── common
│ │ ├── templatetags // 通用template tag
│ │ ├── utils.py // 通用的函数方法
│ │ └── views.py
│ ├── fixtures // 初始化数据目录
│ │ ├── init.json // 初始化项目数据库
│ │ └── fake.json // 生成大量测试数据
│ ├── jumpserver // 项目设置目录
│ │ ├── __init__.py
│ │ ├── settings.py // 项目设置文件
│ │ ├── urls.py // 项目入口urlconf
│ │ └── wsgi.py
│ ├── manage.py
│ ├── static // 项目静态资源目录
│ ├── i18n // 项目多语言目录
│ └── templates // 项目模板目录
\ No newline at end of file
Jumpserver 项目规范(Draft)
============================
语言框架
--------
1. Python 3.6.1 (当前最新)
2. Django 1.11 (当前最新)
3. Flask 0.12 Luna (当前最新)
4. Paramiko 2.12 Coco (当前最新)
Django规范
----------
1. 尽量使用Class Base View编程,更少代码
2. 使用Django Form
3. 每个url独立命名,不要硬编码,同理static也是
4. 数据库表名手动指定,不要使用默认
5. 代码优雅简洁
6. 注释明确优美
7. 测试案例尽可能完整
8. 尽可能利用Django造好的轮子
代码风格
--------
Python方面大致的风格,我们采用pocoo的\ `Style
Guidance`_\ ,但是有些细节部分会尽量放开 参考国内翻译
基本的代码布局
~~~~~~~~~~~~~~
缩进
^^^^
1. Python严格采用4个空格的缩进,任何python代码都都必须遵守此规定。
2. web部分代码(HTML, CSS,
JavaScript),Node.js采用2空格缩进,同样不使用tab (:raw-latex:`\t`)。
之所以与Python不同,是因为js中有大量回调式的写法,2空格可以显著降低视觉上的负担。
最大行长度
^^^^^^^^^^
按PEP8规范,Python一般限制最大79个字符,
但是Django的命名,url等通常比较长,
而且21世纪都是宽屏了,所以我们限制最大120字符
**补充说明:HTML代码不受此规范约束。**
长语句缩进
^^^^^^^^^^
编写长语句时,可以使用换行符()换行。在这种情况下,下一行应该与上一行的最后
一个“.”句点或“=”对齐,或者是缩进4个空格符
::
this_is_a_very_long(function_call, 'with many parameters') \
.that_returns_an_object_with_an_attribute
MyModel.query.filter(MyModel.scalar > 120) \
.order_by(MyModel.name.desc()) \
.limit(10)
如果你使用括号“()”或花括号“{}”为长语句换行,那么下一行应与括号或花括号对齐:
::
this_is_a_very_long(function_call, 'with many parameters',
23, 42, 'and even more')
对于元素众多的列表或元组,在第一个“[”或“(”之后马上换行:
::
items = [
'this is the first', 'set of items', 'with more items',
'to come in this line', 'like this'
]
.. _Style Guidance: http://www.pocoo.org/internal/styleguide/
空行
^^^^
顶层函数与类之间空两行,此外都只空一行。不要在代码中使用太多的空行来区分不同的逻辑模块。
::
def hello(name):
print 'Hello %s!' % name
def goodbye(name):
print 'See you %s.' % name
class MyClass(object):
"""This is a simple docstring."""
def __init__(self, name):
self.name = name
def get_annoying_name(self):
return self.name.upper() + '!!!!111'
语句和表达式
~~~~~~~~~~~~
一般空格规则
^^^^^^^^^^^^
1. 单目运算符与运算对象之间不空格(例如,-,~等),即使单目运算符位于括号内部也一样。
2. 双目运算符与运算对象之间要空格。
::
exp = -1.05
value = (item_value / item_count) * offset / exp
value = my_list[index]
value = my_dict['key']
比较
^^^^
1. 任意类型之间的比较,使用“==”和“!=”。
2. 与单例(singletons)进行比较时,使用is和is not。
3. 永远不要与True或False进行比较(例如,不要这样写:foo ==
False,而应该这样写:not foo)。
否定成员关系检查
^^^^^^^^^^^^^^^^
使用foo not in bar,而不是not foo in bar。
命名约定
~~~~~~~~
1. 类名称:采用骆驼拼写法(CamelCase),首字母缩略词保持大写不变(HTTPWriter,而不是HttpWriter)。
2. 变量名:小写_以及_下划线(lowercase_with_underscores)。
3. 方法与函数名:小写_以及_下划线(lowercase_with_underscores)。
4. 常量:大写_以及_下划线(UPPERCASE_WITH_UNDERSCORES)。
5. 预编译的正则表达式:name_re。
6. 受保护的元素以一个下划线为前缀。双下划线前缀只有定义混入类(mixin
classes)时才使用。
7. 如果使用关键词(keywords)作为类名称,应在名称后添加后置下划线(trailing
underscore)。
允许与内建变量重名,不要在变量名后添加下划线进行区分。如果函数需要访问重名的内建变量,请将内建变量重新绑定为其他名称。
8. 命名要有寓意, 不使用拼音,不使用无意义简单字母命名 (循环中计数例外 for
i in)
9. 命名缩写要谨慎, 尽量是大家认可的缩写
函数和方法的参数:
^^^^^^^^^^^^^^^^^^
1. 类方法:cls为第一个参数。
2. 实例方法:self为第一个参数。
3. property函数中使用匿名函数(lambdas)时,匿名函数的第一个参数可以用x替代,
例如:display_name = property(lambda x: x.real_name or x.username)。
文档注释(Docstring,即各方法,类的说明文档注释)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
所有文档字符串均以reStructuredText格式编写,方便Sphinx处理。文档字符串的行数不同,布局也不一样。
如果只有一行,代表字符串结束的三个引号与代表字符串开始的三个引号在同一行。
如果为多行,文档字符串中的文本紧接着代表字符串开始的三个引号编写,代表字符串结束的三个引号则自己独立成一行。
(有能力尽可能用英文, 否则请中文优雅注释)
::
def foo():
"""This is a simple docstring."""
def bar():
"""This is a longer docstring with so much information in there
that it spans three lines. In this case, the closing triple quote
is on its own line.
"""
文档字符串应分成简短摘要(尽量一行)和详细介绍。如果必要的话,摘要与详细介绍之间空一行。
模块头部
~~~~~~~~
模块文件的头部包含有utf-8编码声明(如果模块中使用了非ASCII编码的字符,建议进行声明),以及标准的文档字符串。
::
# -*- coding: utf-8 -*-
"""
package.module
~~~~~~~~~~~~~~
A brief description goes here.
:copyright: (c) YEAR by AUTHOR.
:license: LICENSE_NAME, see LICENSE_FILE for more details.
"""
注释(comment)
~~~~~~~~~~~~~
注释的规范与文档字符串编写规范类似。二者均以reStructuredText格式编写。
如果使用注释来编写类属性的文档,请在#符号后添加一个冒号“:”。
(有能力尽可能用英文, 否则请中文优雅注释)
::
class User(object):
#: the name of the user as unicode string
name = Column(String)
#: the sha1 hash of the password + inline salt
pw_hash = Column(String)
\ No newline at end of file
快速安装
==========================
Jumpserver 封装了一个All in one Docker,可以快速启动。该镜像集成了所有需要的组件,可以使用外置db和redis
Tips: 不建议在生产中使用
Docker 安装见: `Docker官方安装文档 <https://docs.docker.com/install/>`_
快速启动
```````````````
使用root命令行输入::
$ docker run -p 8080:80 -p 2222:2222 jumpserver/jumpserver:0.5.0-beta2
访问
```````````````
浏览器访问: http://localhost:8080
ssh访问: ssh -p 2222 localhost
额外环境变量
```````````````
- DB_ENGINE = mysql
- DB_HOST = mysql_host
- DB_PORT = 3306
- DB_USER = xxx
- DB_PASSWORD = xxxx
- DB_NAME = jumpserver
- REDIS_HOST = ''
- REDIS_PORT = ''
- REDIS_PASSWORD = ''
::
docker run -p 8080:80 -p 2222:2222 -e DB_ENGINE=mysql -e DB_HOST=192.168.1.1 -e DB_PORT=3306 -e DB_USER=root -e DB_PASSWORD=xxx -e DB_NAME=jumpserver jumpserver/jumpserver:0.5.0-beta2
仓库地址
```````````````
https://github.com/jumpserver/Dockerfile
一步一步安装
--------------------------
环境
~~~~
- 系统: CentOS 7
- IP: 192.168.244.144
- 关闭 selinux和防火墙
::
# CentOS 7
$ setenforce 0 # 可以设置配置文件永久关闭
$ systemctl stop iptables.service
$ systemctl stop firewalld.service
# CentOS6
$ setenforce 0
$ service iptables stop
一. 准备Python3和Python虚拟环境
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**1.1 安装依赖包**
::
$ yum -y install wget sqlite-devel xz gcc automake zlib-devel openssl-devel epel-release
**1.2 编译安装**
::
$ wget https://www.python.org/ftp/python/3.6.1/Python-3.6.1.tar.xz
$ tar xvf Python-3.6.1.tar.xz && cd Python-3.6.1
$ ./configure && make && make install
**1.3 建立python虚拟环境**
因为CentOS
6/7自带的是Python2,而Yum等工具依赖原来的Python,为了不扰乱原来的环境我们来使用Python虚拟环境
::
$ cd /opt
$ python3 -m venv py3
$ source /opt/py3/bin/activate
# 看到下面的提示符代表成功,以后运行jumpserver都要先运行以上source命令,以下所有命令均在该虚拟环境中运行
(py3) [root@localhost py3]#
二. 安装Jumpserver 0.5.0
~~~~~~~~~~~~~~~~~~~~~~~~
**2.1 下载或clone项目**
项目提交较多git clone时较大,你可以选择去github项目页面直接下载
zip包,我的网速好,我直接clone了
::
$ cd /opt/
$ git clone --depth=1 https://github.com/jumpserver/jumpserver.git && cd jumpserver && git checkout dev
**2.2 安装依赖rpm包**
::
$ cd /opt/jumpserver/requirements
$ yum -y install $(cat rpm_requirements.txt) # 如果没有任何报错请继续
**2.3 安装python库依赖**
::
$ pip install -r requirements.txt # 不要指定-i参数,因为镜像上可能没有最新的包,如果没有任何报错请继续
**2.4 安装Redis, jumpserver使用redis做cache和celery broker**
::
$ yum -y install redis
$ service redis start
**2.5 安装MySQL**
本教程使用mysql作为数据库,如果不使用mysql可以跳过相关mysql安装和配置
::
# centos7
$ yum -y install mariadb mariadb-devel mariadb-server # centos7下安装的是mariadb
$ service mariadb start
# centos6
$ yum -y install mysql mysql-devel mysql-server
$ service mysqld start
**2.6 创建数据库 jumpserver并授权**
::
$ mysql
> create database jumpserver default charset 'utf8';
> grant all on jumpserver.* to 'jumpserver'@'127.0.0.1' identified by 'somepassword';
**2.7 修改jumpserver配置文件**
::
$ cd /opt/jumpserver
$ cp config_example.py config.py
$ vi config.py # 我们计划修改 DevelopmentConfig中的配置,因为默认jumpserver是使用该配置,它继承自Config
**注意: 配置文件是python格式,不要用tab,而要用空格** **注意:
配置文件是python格式,不要用tab,而要用空格** **注意:
配置文件是python格式,不要用tab,而要用空格**
::
class DevelopmentConfig(Config):
DEBUG = True
DB_ENGINE = 'mysql'
DB_HOST = '127.0.0.1'
DB_PORT = 3306
DB_USER = 'jumpserver'
DB_PASSWORD = 'somepassword'
DB_NAME = 'jumpserver'
...
config = DevelopmentConfig() # 确保使用的是刚才设置的配置文件
**2.8 生成数据库表结构和初始化数据**
::
$ cd /opt/jumpserver/utils
$ bash make_migrations.sh
**2.9 运行Jumpserver**
::
$ cd /opt/jumpserver
$ python run_server.py all
运行不报错,请浏览器访问 http://192.168.244.144:8080/
(这里只是jumpserver, 没有web terminal,所以访问web terminal会报错)
账号:admin 密码: admin
三. 安装 SSH Server和Web Socket Server: Coco
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**3.1 下载clone项目**
新开一个终端,连接测试机,别忘了 source /opt/py3/bin/activate
::
$ cd /opt
$ git clone https://github.com/jumpserver/coco.git && cd coco && git checkout dev
**3.2 安装依赖**
::
$ cd /opt/coco/requirements $ yum -y install $(cat rpm_requirements.txt) $ pip install requirements.txt
**3.2 安装依赖**
::
$ cd /opt/coco/requirements
$ yum -y install $(cat rpm_requirements.txt)
$ pip install -r requirements.txt
**3.3 查看配置文件并运行**
::
$ cd /opt/coco
$ cp conf_example.py conf.py
$ python run_server.py
这时需要去
jumpserver管理后台-终端-终端(http://192.168.244.144:8080/terminal/terminal/)接受coco的注册
::
Coco version 0.4.0, more see https://www.jumpserver.org
Starting ssh server at 0.0.0.0:2222
Quit the server with CONTROL-C.
**3.4 测试连接**
::
$ ssh -p2222 admin@192.168.244.144
密码: admin
如果是用在windows下,Xshell terminal登录语法如下
$ssh admin@192.168.244.144 2222
密码: admin
如果能登陆代表部署成功
四. 安装 Web Terminal 前端: Luna
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Luna已改为纯前端,需要nginx来运行访问
下载 release包,直接解压,不需要编译
访问 https://github.com/jumpserver/luna/releases,下载对应release包
4.1 解压luna
::
$ pwd
/opt/
$ tar xvf luna.tar.gz
$ ls /opt/luna
...
五. 安装Windows支持组件
~~~~~~~~~~~~~~~~~~~~~~~
使用docker启动 guacamole
.. code:: shell
docker run \
-p 8080:8080 \
-e JUMPSERVER_SERVER=http://<jumpserver>:8080 \
jumpserver/guacamole
这里所需要注意的是guacamole暴露出来的端口是8080,若与jumpserver部署在同一主机上自定义一下。
修改JUMPSERVER_SERVER的配置,填上jumpserver的内网地址
六. 配置 nginx 整合各组件
~~~~~~~~~~~~~~~~~~~~~~~~~
6.1 安装nginx 根据喜好选择安装方式和版本
6.2 配置文件
::
server {
listen 80;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location /luna/ {
try_files $uri / /index.html;
alias /opt/luna/;
}
location /media/ {
add_header Content-Encoding gzip;
root /opt/jumpserver/data/;
}
location /static/ {
root /opt/jumpserver/data/;
}
location /socket.io/ {
proxy_pass http://localhost:5000/socket.io/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /guacamole/ {
proxy_pass http://<guacamole>:8080/;
}
location / {
proxy_pass http://localhost:8080;
}
}
6.3 运行 nginx
6.4 访问 http://192.168.244.144
\ No newline at end of file
升级
----
1. 升级 jumpserver
::
$ git pull && pip install -r requirements/requirements.txt && cd utils && sh make_migrations.sh
2. 升级 coco
::
$ git pull && cd requirements && pip install -r requirements.txt # 不要指定 -i参数
3. 升级 luna
重新下载release包
\ No newline at end of file
个人资产
=========
这里介绍用户个人资产相关的功能。
.. contents:: Topics
.. _view_personal_assets:
查看个人资产
````````````
登录个人用户,默认展示个人资产列表。点击主机名,查看资产的详细信息。
.. _host_login:
主机登录
`````````
点解页面左侧的"Web终端",进入主机登录页,然后点击页面右侧的主机IP地址,连接主机,页面右侧会展示当前连接的终端信息。
.. _host_logout:
主机登出
`````````
在主机登录页面,选择左上角的“服务器”按钮,出现两个选项,一个“断开链接“按钮,断开当前连接的主机;另一个”断开所有链接“,断开当前所有连接的主机。
\ No newline at end of file
用户使用文档
=============
这部分给您介绍Jumpserver的用户管理模块的使用方法。
.. toctree::
:maxdepth: 1
user_asset
user_info
\ No newline at end of file
个人信息
=========
这里介绍个人信息相关的功能。
.. contents:: Topics
.. _view_personal_info:
查看个人信息
````````````
点击页面左侧的“个人信息”,查看用户的个人信息、SSH密钥。
.. _modify_personal_info:
修改个人信息
````````````
在个人信息页,点击页面右上角的“设置”按钮,进入个人信息修改页面,填写个人信息,点击“提交”按钮,完成个人信息修改。
.. _update_password:
更新密码
`````````
在个人信息页,点击页面右上角的“重置密码“按钮,进入密码更新页面,填写原来密码、新密码等信息,点击“提交”按钮,完成密码更新。
.. _update_ssh_key:
密钥更新
`````````
在个人信息页,点击页面左上角的“重置SSH密钥“按钮,进入密钥更新页面,填写SSH公钥,点击“提交”按钮,完成密钥更新。
\ No newline at end of file
......@@ -56,8 +56,8 @@ uritemplate==3.0.0
urllib3==1.22
vine==1.1.4
gunicorn==19.7.1
https://github.com/celery/django-celery-beat/zipball/master#egg=django-celery-beat
#django_celery_beat==1.1.0
#https://github.com/celery/django-celery-beat/zipball/master#egg=django-celery-beat
django_celery_beat==1.1.1
ephem==3.7.6.0
python-gssapi==0.6.4
jms-es-sdk
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