Unverified Commit 6f4273c6 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #124 from jumpserver/dev

Dev
parents 5f8d1227 88a9e2d3
...@@ -183,13 +183,13 @@ class TelnetConnection: ...@@ -183,13 +183,13 @@ class TelnetConnection:
r'incorrect|failed|失败|错误', re.I r'incorrect|failed|失败|错误', re.I
) )
self.username_pattern = re.compile( self.username_pattern = re.compile(
r'login:\s*$|username:\s*$|用户名:\s*$|账\s*号:\s*$', re.I r'login:?\s*$|username:?\s*$|用户名:?\s*$|账\s*号:?\s*$', re.I
) )
self.password_pattern = re.compile( self.password_pattern = re.compile(
r'password:\s*$|passwd:\s*$|密\s*码:\s*$', re.I r'Password:?\s*$|passwd:?\s*$|密\s*码:?\s*$', re.I
) )
self.success_pattern = re.compile( self.success_pattern = re.compile(
r'Last\s*login|success|成功', re.I r'Last\s*login|success|成功|#|\$', re.I
) )
def get_socket(self): def get_socket(self):
......
...@@ -40,7 +40,6 @@ class ElFinderConnector: ...@@ -40,7 +40,6 @@ class ElFinderConnector:
'uplMaxSize': '10M', 'uplMaxSize': '10M',
'options': { 'options': {
'separator': '/', 'separator': '/',
'disabled': [],
'archivers': { 'archivers': {
'create': [], 'create': [],
'extract': [] 'extract': []
......
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
import socket import socket
import threading import threading
import os import os
import math
import time
from treelib import Tree
from . import char from . import char
from .config import config from .config import config
...@@ -17,19 +20,35 @@ from .proxy import ProxyServer ...@@ -17,19 +20,35 @@ from .proxy import ProxyServer
logger = get_logger(__file__) logger = get_logger(__file__)
PAGE_DOWN = 'down'
PAGE_UP = 'up'
BACK = 'back'
PROXY = 'proxy'
class InteractiveServer: class InteractiveServer:
_sentinel = object() _sentinel = object()
def __init__(self, client): def __init__(self, client):
self.client = client self.client = client
self.assets = None
self.closed = False self.closed = False
self._search_result = None self._search_result = None
self.nodes = None self.nodes = None
self.get_user_assets_async() self.offset = 0
self.limit = 100
self.assets_list = []
self.finish = False
self.page = 1
self.total_assets = 0
self.total_count = 0 # 分页展示中用来存放数目总条数
self.nodes_tree = None # 授权节点树
self.get_user_assets_paging_async()
self.get_user_nodes_async() self.get_user_nodes_async()
@property
def page_size(self):
return self.client.request.meta['height'] - 8
@property @property
def search_result(self): def search_result(self):
if self._search_result: if self._search_result:
...@@ -64,7 +83,7 @@ class InteractiveServer: ...@@ -64,7 +83,7 @@ class InteractiveServer:
_("{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} search, such as: /ip.{R}"), _("{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} search, such as: /ip.{R}"),
_("{T}3) Enter {green}p{end} to display the host you have permission.{R}"), _("{T}3) Enter {green}p{end} to display the host you have permission.{R}"),
_("{T}4) Enter {green}g{end} to display the node that you have permission.{R}"), _("{T}4) Enter {green}g{end} to display the node that you have permission.{R}"),
_("{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under the node, such as g1.{R}"), _("{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the node, such as g1.{R}"),
_("{T}6) Enter {green}s{end} Chinese-english switch.{R}"), _("{T}6) Enter {green}s{end} Chinese-english switch.{R}"),
_("{T}7) Enter {green}h{end} help.{R}"), _("{T}7) Enter {green}h{end} help.{R}"),
_("{T}0) Enter {green}q{end} exit.{R}") _("{T}0) Enter {green}q{end} exit.{R}")
...@@ -87,7 +106,7 @@ class InteractiveServer: ...@@ -87,7 +106,7 @@ class InteractiveServer:
elif opt in ['p', 'P', '']: elif opt in ['p', 'P', '']:
self.display_assets() self.display_assets()
elif opt in ['g', 'G']: elif opt in ['g', 'G']:
self.display_nodes() self.display_nodes_tree()
elif opt.startswith("g") and opt.lstrip("g").isdigit(): elif opt.startswith("g") and opt.lstrip("g").isdigit():
self.display_node_assets(int(opt.lstrip("g"))) self.display_node_assets(int(opt.lstrip("g")))
elif opt in ['q', 'Q', 'exit', 'quit']: elif opt in ['q', 'Q', 'exit', 'quit']:
...@@ -101,38 +120,36 @@ class InteractiveServer: ...@@ -101,38 +120,36 @@ class InteractiveServer:
self.search_and_proxy(opt) self.search_and_proxy(opt)
def search_assets(self, q): def search_assets(self, q):
if self.assets is None: if not self.finish:
self.get_user_assets() assets = app_service.get_search_user_granted_assets(self.client.user, q)
return assets
assets = self.assets_list
result = [] result = []
# 所有的 # 所有的
if q in ('', None): if q in ('', None):
result = self.assets result = assets
# 用户输入的是数字,可能想使用id唯一键搜索
elif q.isdigit() and self.search_result and \
len(self.search_result) >= int(q):
result = [self.search_result[int(q) - 1]]
# 全匹配到则直接返回全匹配的 # 全匹配到则直接返回全匹配的
if len(result) == 0: if len(result) == 0:
_result = [asset for asset in self.assets _result = [asset for asset in assets
if is_obj_attr_eq(asset, q)] if is_obj_attr_eq(asset, q)]
if len(_result) == 1: if len(_result) == 1:
result = _result result = _result
# 最后模糊匹配 # 最后模糊匹配
if len(result) == 0: if len(result) == 0:
result = [asset for asset in self.assets result = [asset for asset in assets
if is_obj_attr_has(asset, q)] if is_obj_attr_has(asset, q)]
self.search_result = result return result
def display_assets(self): def display_assets(self):
""" """
Display user all assets Display user all assets
:return: :return:
""" """
self.search_and_display('') self.display_result_paging(self.assets_list)
def display_nodes(self): def display_nodes(self):
if self.nodes is None: if self.nodes is None:
...@@ -154,17 +171,31 @@ class InteractiveServer: ...@@ -154,17 +171,31 @@ class InteractiveServer:
self.client.send(wr(format_with_zh(size_list, *data))) self.client.send(wr(format_with_zh(size_list, *data)))
self.client.send(wr(_("Total: {}").format(len(self.nodes)), before=1)) self.client.send(wr(_("Total: {}").format(len(self.nodes)), before=1))
def display_nodes_tree(self):
if self.nodes is None:
self.get_user_nodes()
if not self.nodes:
self.client.send(wr(_('No Nodes'), before=1))
return
self.nodes_tree.show(key=lambda node: node.identifier)
self.client.send(wr(title(_("Node: [ ID.Name(Asset amount) ]")), before=1))
self.client.send(wr(self.nodes_tree._reader.replace('\n', '\r\n'), before=1))
prompt = _("Tips: Enter g+NodeID to display the host under the node, such as g1")
self.client.send(wr(title(prompt), before=1))
def display_node_assets(self, _id): def display_node_assets(self, _id):
if self.nodes is None: if self.nodes is None:
self.get_user_nodes() self.get_user_nodes()
if _id > len(self.nodes) or _id <= 0: if _id > len(self.nodes) or _id <= 0:
msg = wr(warning(_("There is no matched node, please re-enter"))) msg = wr(warning(_("There is no matched node, please re-enter")))
self.client.send(msg) self.client.send(msg)
self.display_nodes() self.display_nodes_tree()
return return
self.search_result = self.nodes[_id - 1].assets_granted assets = self.nodes[_id - 1].assets_granted
self.display_search_result() self.display_result_paging(assets)
def display_search_result(self): def display_search_result(self):
sort_by = config["ASSET_LIST_SORT_BY"] sort_by = config["ASSET_LIST_SORT_BY"]
...@@ -191,16 +222,31 @@ class InteractiveServer: ...@@ -191,16 +222,31 @@ class InteractiveServer:
asset.system_users_name_list, asset.comment asset.system_users_name_list, asset.comment
] ]
self.client.send(wr(format_with_zh(size_list, *data))) self.client.send(wr(format_with_zh(size_list, *data)))
self.client.send(wr(_("Total: {} Match: {}").format(
len(self.assets), len(self.search_result)), before=1) total_page = math.ceil(self.total_count/self.page_size)
self.client.send(wr(title(_("Page: {}, Count: {}, Total Page: {}, Total Count: {}").format(
self.page, len(self.search_result), total_page, self.total_count)), before=1)
) )
def search_and_display(self, q): def search_and_display(self, q):
self.search_assets(q) assets = self.search_assets(q)
self.display_search_result() self.display_result_paging(assets)
def get_user_nodes(self): def get_user_nodes(self):
self.nodes = app_service.get_user_asset_groups(self.client.user) self.nodes = app_service.get_user_asset_groups(self.client.user)
self.sort_nodes()
self.construct_nodes_tree()
def sort_nodes(self):
self.nodes = sorted(self.nodes, key=lambda node: node.key)
def construct_nodes_tree(self):
self.nodes_tree = Tree()
for index, node in enumerate(self.nodes):
tag = "{}.{}({})".format(index+1, node.name, node.assets_amount)
key = node.key
parent_key = key[:node.key.rfind(':')] or None
self.nodes_tree.create_node(tag=tag, identifier=key, data=node, parent=parent_key)
def get_user_nodes_async(self): def get_user_nodes_async(self):
thread = threading.Thread(target=self.get_user_nodes) thread = threading.Thread(target=self.get_user_nodes)
...@@ -217,14 +263,24 @@ class InteractiveServer: ...@@ -217,14 +263,24 @@ class InteractiveServer:
asset.system_users_granted = system_users_cleaned asset.system_users_granted = system_users_cleaned
return assets return assets
def get_user_assets(self): def get_user_assets_paging(self):
self.assets = app_service.get_user_assets(self.client.user) while not self.closed:
logger.debug("Get user {} assets total: {}".format( assets, total = app_service.get_user_assets_paging(
self.client.user, len(self.assets)) self.client.user, offset=self.offset, limit=self.limit
) )
logger.info('Get user assets paging async: {}'.format(len(assets)))
def get_user_assets_async(self): if not assets:
thread = threading.Thread(target=self.get_user_assets) logger.info('Get user assets paging async finished.')
self.finish = True
return
if not self.total_assets:
self.total_assets = total
self.total_count = total
self.assets_list.extend(assets)
self.offset += self.limit
def get_user_assets_paging_async(self):
thread = threading.Thread(target=self.get_user_assets_paging)
thread.start() thread.start()
def choose_system_user(self, system_users): def choose_system_user(self, system_users):
...@@ -251,9 +307,9 @@ class InteractiveServer: ...@@ -251,9 +307,9 @@ class InteractiveServer:
self.client.send(wr("{} {}".format(index, system_user.name))) self.client.send(wr("{} {}".format(index, system_user.name)))
def search_and_proxy(self, opt): def search_and_proxy(self, opt):
self.search_assets(opt) assets = self.search_assets(opt)
if self.search_result and len(self.search_result) == 1: if assets and len(assets) == 1:
asset = self.search_result[0] asset = assets[0]
self.search_result = None self.search_result = None
if asset.platform == "Windows": if asset.platform == "Windows":
self.client.send(warning( self.client.send(warning(
...@@ -263,7 +319,111 @@ class InteractiveServer: ...@@ -263,7 +319,111 @@ class InteractiveServer:
return return
self.proxy(asset) self.proxy(asset)
else: else:
self.display_search_result() self.display_result_paging(assets)
def display_result_paging(self, result_list):
if result_list is self.assets_list:
self.total_count = self.total_assets
else:
if len(result_list) == 0:
return
self.total_count = len(result_list)
action = PAGE_DOWN
gen_result = self.get_result_page_down_or_up(result_list)
while True:
try:
page, result = gen_result.send(action)
except TypeError:
try:
page, result = next(gen_result)
except StopIteration:
logger.info('No Assets')
self.display_banner()
self.client.send(wr(_("No Assets"), before=1))
return None
except StopIteration:
logger.info('Back display result paging.')
self.display_banner()
return None
self.display_result_of_page(page, result)
action = self.get_user_action()
def get_result_page_down_or_up(self, result_list):
left = 0
page = 1
page_up_size = 0 # 记录上一页大小
while True:
right = left + self.page_size
result = result_list[left:right]
if not result and (result_list is self.assets_list) and self.finish and self.total_assets == 0:
# 无授权资产
return None, None
elif not result and (result_list is self.assets_list) and self.finish:
# 上一页是最后一页
left -= page_up_size
page -= 1
continue
elif not result and (result_list is self.assets_list) and not self.finish:
# 还有下一页(暂时没有加载完),需要等待
time.sleep(1)
continue
elif not result and (result_list is not self.assets_list):
# 上一页是最后一页
left -= page_up_size
page -= 1
continue
else:
# 其他4中情况,返回assets
action = yield (page, result)
if action == BACK:
return None, None
elif action == PAGE_UP:
if page <= 1:
# 已经是第一页了
page = 1
left = 0
else:
page -= 1
left -= self.page_size
else:
# PAGE_DOWN
page += 1
left += len(result)
page_up_size = len(result)
def display_result_of_page(self, page, result):
self.client.send(char.CLEAR_CHAR)
self.page = page
self.search_result = result
self.display_search_result()
self.display_prompt_of_page()
def display_prompt_of_page(self):
self.client.send(wr(_('Tips: Enter the asset ID and log directly into the asset.'), before=1))
prompt_page_up = _("Page up: P/p")
prompt_page_down = _("Page down: Enter|N/n")
prompt_back = _("BACK: b/q")
prompts = [prompt_page_up, prompt_page_down, prompt_back]
prompt = '\t'.join(prompts)
self.client.send(wr(prompt, before=1))
def get_user_action(self):
opt = net_input(self.client, prompt=':')
if opt in ('p', 'P'):
return PAGE_UP
elif opt in ('b', 'q'):
return BACK
elif opt.isdigit() and self.search_result and 0 < int(opt) <= len(self.search_result):
self.proxy(self.search_result[int(opt)-1])
return BACK
else:
# PAGE_DOWN
return PAGE_DOWN
def proxy(self, asset): def proxy(self, asset):
system_user = self.choose_system_user(asset.system_users_granted) system_user = self.choose_system_user(asset.system_users_granted)
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-10 15:22+0800\n" "POT-Creation-Date: 2018-10-31 11:49+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n" "PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n" "Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/en/LC\n" "Language-Team: Language locale/en/LC\n"
...@@ -16,11 +16,11 @@ msgstr "" ...@@ -16,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: coco/app.py:141 #: coco/app.py:135
msgid "Connect idle more than {} minutes, disconnect" msgid "Connect idle more than {} minutes, disconnect"
msgstr "" msgstr ""
#: coco/interactive.py:61 #: coco/interactive.py:80
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"\n" "\n"
...@@ -28,115 +28,143 @@ msgid "" ...@@ -28,115 +28,143 @@ msgid ""
"{end}{R}{R}" "{end}{R}{R}"
msgstr "" msgstr ""
#: coco/interactive.py:63 #: coco/interactive.py:82
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, " "{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
"Comment{end} to search login(if unique).{R}" "Comment{end} to search login(if unique).{R}"
msgstr "" msgstr ""
#: coco/interactive.py:64 #: coco/interactive.py:83
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} " "{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
"search, such as: /ip.{R}" "search, such as: /ip.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:65 #: coco/interactive.py:84
#, python-brace-format #, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}" msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:66 #: coco/interactive.py:85
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}" "{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:67 #: coco/interactive.py:86
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under " "{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the "
"the node, such as g1.{R}" "node, such as g1.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:68 #: coco/interactive.py:87
#, python-brace-format #, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}" msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:69 #: coco/interactive.py:88
#, python-brace-format #, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}" msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:70 #: coco/interactive.py:89
#, python-brace-format #, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}" msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:142 #: coco/interactive.py:159
msgid "No" msgid "No"
msgstr "" msgstr ""
#: coco/interactive.py:149 #: coco/interactive.py:166
msgid "Name" msgid "Name"
msgstr "" msgstr ""
#: coco/interactive.py:149 #: coco/interactive.py:166
msgid "Assets" msgid "Assets"
msgstr "" msgstr ""
#: coco/interactive.py:155 #: coco/interactive.py:172
msgid "Total: {}" msgid "Total: {}"
msgstr "" msgstr ""
#: coco/interactive.py:161 #: coco/interactive.py:177
msgid "Node [ ID.Name(Asset) ]"
msgstr ""
#: coco/interactive.py:179
msgid "Enter g+NodeID to display the host under the node, such as g1."
msgstr ""
#: coco/interactive.py:186
msgid "There is no matched node, please re-enter" msgid "There is no matched node, please re-enter"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "Hostname" msgid "Hostname"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "IP" msgid "IP"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "LoginAs" msgid "LoginAs"
msgstr "" msgstr ""
#: coco/interactive.py:186 #: coco/interactive.py:211
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
#: coco/interactive.py:194 #: coco/interactive.py:221
msgid "Total: {} Match: {}" msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "" msgstr ""
#: coco/interactive.py:237 #: coco/interactive.py:296
msgid "Select a login:: " msgid "Select a login:: "
msgstr "" msgstr ""
#: coco/interactive.py:260 #: coco/interactive.py:319
msgid "" msgid ""
"Terminal does not support login Windows, please use web terminal to access" "Terminal does not support login Windows, please use web terminal to access"
msgstr "" msgstr ""
#: coco/interactive.py:271 #: coco/interactive.py:401
msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr ""
#: coco/interactive.py:402
msgid "Page up: P/p"
msgstr ""
#: coco/interactive.py:403
msgid "Page down: Enter|N/n"
msgstr ""
#: coco/interactive.py:404
msgid "BACK: B/b"
msgstr ""
#: coco/interactive.py:425
msgid "No system user" msgid "No system user"
msgstr "" msgstr ""
#: coco/proxy.py:88 #: coco/models.py:247
msgid "Command `{}` is forbidden ........"
msgstr ""
#: coco/proxy.py:89
msgid "No permission" msgid "No permission"
msgstr "" msgstr ""
#: coco/proxy.py:130 #: coco/proxy.py:131
msgid "Connecting to {}@{} {:.1f}" msgid "Connecting to {}@{} {:.1f}"
msgstr "" msgstr ""
......
...@@ -7,7 +7,7 @@ msgid "" ...@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-10-10 15:22+0800\n" "POT-Creation-Date: 2018-10-31 11:49+0800\n"
"PO-Revision-Date: 2018-08-10 10:42+0800\n" "PO-Revision-Date: 2018-08-10 10:42+0800\n"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n" "Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/zh\n" "Language-Team: Language locale/zh\n"
...@@ -16,11 +16,11 @@ msgstr "" ...@@ -16,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: coco/app.py:141 #: coco/app.py:135
msgid "Connect idle more than {} minutes, disconnect" msgid "Connect idle more than {} minutes, disconnect"
msgstr "空闲时间超过 {} 分钟,断开连接" msgstr "空闲时间超过 {} 分钟,断开连接"
#: coco/interactive.py:61 #: coco/interactive.py:80
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"\n" "\n"
...@@ -30,7 +30,7 @@ msgstr "" ...@@ -30,7 +30,7 @@ msgstr ""
"\n" "\n"
"{T}{T}{title} {user}, 欢迎使用Jumpserver开源跳板机系统 {end}{R}{R}" "{T}{T}{title} {user}, 欢迎使用Jumpserver开源跳板机系统 {end}{R}{R}"
#: coco/interactive.py:63 #: coco/interactive.py:82
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, " "{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
...@@ -39,7 +39,7 @@ msgstr "" ...@@ -39,7 +39,7 @@ msgstr ""
"{T}1) 输入 {green}ID{end} 直接登录 或 输入{green}部分 IP,主机名,备注{end} 进" "{T}1) 输入 {green}ID{end} 直接登录 或 输入{green}部分 IP,主机名,备注{end} 进"
"行搜索登录(如果唯一).{R}" "行搜索登录(如果唯一).{R}"
#: coco/interactive.py:64 #: coco/interactive.py:83
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} " "{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
...@@ -48,104 +48,134 @@ msgstr "" ...@@ -48,104 +48,134 @@ msgstr ""
"{T}2) 输入 {green}/{end} + {green}IP, 主机名{end} or {green}备注 {end}搜索. " "{T}2) 输入 {green}/{end} + {green}IP, 主机名{end} or {green}备注 {end}搜索. "
"如: /ip{R}" "如: /ip{R}"
#: coco/interactive.py:65 #: coco/interactive.py:84
#, python-brace-format #, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}" msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr "{T}3) 输入 {green}p{end} 显示您有权限的主机.{R}" msgstr "{T}3) 输入 {green}p{end} 显示您有权限的主机.{R}"
#: coco/interactive.py:66 #: coco/interactive.py:85
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}" "{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr "{T}4) 输入 {green}g{end} 显示您有权限的节点.{R}" msgstr "{T}4) 输入 {green}g{end} 显示您有权限的节点.{R}"
#: coco/interactive.py:67 #: coco/interactive.py:86
#, python-brace-format
msgid "" msgid ""
"{T}5) Enter {green}g{end} + {green}Group ID{end} to display the host under " "{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the "
"the node, such as g1.{R}" "node, such as g1.{R}"
msgstr "{T}5) 输入 {green}g{end} + {green}ID{end} 显示节点下主机. 如: g1{R}" msgstr "{T}5) 输入 {green}g{end} + {green}节点ID{end} 显示节点下主机. 如: g1{R}"
#: coco/interactive.py:68 #: coco/interactive.py:87
#, python-brace-format #, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}" msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr "{T}6) 输入 {green}s{end} 中/英文切换.{R}" msgstr "{T}6) 输入 {green}s{end} 中/英文切换.{R}"
#: coco/interactive.py:69 #: coco/interactive.py:88
#, python-brace-format #, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}" msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr "{T}7) 输入 {green}h{end} 帮助.{R}" msgstr "{T}7) 输入 {green}h{end} 帮助.{R}"
#: coco/interactive.py:70 #: coco/interactive.py:89
#, python-brace-format #, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}" msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "{T}0) 输入 {green}q{end} 退出.{R}" msgstr "{T}0) 输入 {green}q{end} 退出.{R}"
#: coco/interactive.py:142 #: coco/interactive.py:159
msgid "No" msgid "No"
msgstr "无" msgstr "无"
#: coco/interactive.py:149 #: coco/interactive.py:166
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
#: coco/interactive.py:149 #: coco/interactive.py:166
msgid "Assets" msgid "Assets"
msgstr "资产" msgstr "资产"
#: coco/interactive.py:155 #: coco/interactive.py:172
msgid "Total: {}" msgid "Total: {}"
msgstr "总共: {}" msgstr "总共: {}"
#: coco/interactive.py:161 #: coco/interactive.py:177
msgid "Node: [ ID.Name(Asset amount) ]"
msgstr "节点: [ ID.名称(资产数量) ]"
#: coco/interactive.py:179
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr "提示: 输入 g+节点ID 显示节点下主机. 如: g1"
#: coco/interactive.py:186
msgid "There is no matched node, please re-enter" msgid "There is no matched node, please re-enter"
msgstr "没有匹配分组,请重新输入" msgstr "没有匹配分组,请重新输入"
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "IP" msgid "IP"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:197
msgid "LoginAs" msgid "LoginAs"
msgstr "登录用户" msgstr "登录用户"
#: coco/interactive.py:186 #: coco/interactive.py:211
msgid "Comment" msgid "Comment"
msgstr "备注" msgstr "备注"
#: coco/interactive.py:194 #: coco/interactive.py:221
msgid "Total: {} Match: {}" msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "总共: {} 匹配: {}" msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}"
#: coco/interactive.py:237 #: coco/interactive.py:296
msgid "Select a login:: " msgid "Select a login:: "
msgstr "选择一个登录:" msgstr "选择一个登录:"
#: coco/interactive.py:260 #: coco/interactive.py:319
msgid "" msgid ""
"Terminal does not support login Windows, please use web terminal to access" "Terminal does not support login Windows, please use web terminal to access"
msgstr "终端不支持登录windows, 请使用web terminal访问" msgstr "终端不支持登录windows, 请使用web terminal访问"
#: coco/interactive.py:271 #: coco/interactive.py:401
msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "提示: 输入资产ID,直接登录资产."
#: coco/interactive.py:402
msgid "Page up: P/p"
msgstr "上一页: P/p"
#: coco/interactive.py:403
msgid "Page down: Enter|N/n"
msgstr "下一页: Enter|N/n"
#: coco/interactive.py:404
msgid "BACK: B/b"
msgstr "返回: B/b"
#: coco/interactive.py:425
msgid "No system user" msgid "No system user"
msgstr "没有系统用户" msgstr "没有系统用户"
#: coco/proxy.py:88 #: coco/models.py:247
msgid "Command `{}` is forbidden ........"
msgstr ""
#: coco/proxy.py:89
msgid "No permission" msgid "No permission"
msgstr "没有权限" msgstr "没有权限"
#: coco/proxy.py:130 #: coco/proxy.py:131
msgid "Connecting to {}@{} {:.1f}" msgid "Connecting to {}@{} {:.1f}"
msgstr "开始连接到 {}@{} {:.1f}" msgstr "开始连接到 {}@{} {:.1f}"
#: coco/session.py:143 #: coco/session.py:143
msgid "Terminated by administrator" msgid "Terminated by administrator"
msgstr "被管理员中断" msgstr "被管理员中断"
#~ msgid "Total: {} Match: {}"
#~ msgstr "总共: {} 匹配: {}"
...@@ -18,8 +18,8 @@ idna==2.6 ...@@ -18,8 +18,8 @@ idna==2.6
itsdangerous==0.24 itsdangerous==0.24
Jinja2==2.10 Jinja2==2.10
jmespath==0.9.3 jmespath==0.9.3
jms-storage==0.0.19 jms-storage==0.0.20
jumpserver-python-sdk==0.0.50 jumpserver-python-sdk==0.0.51
MarkupSafe==1.0 MarkupSafe==1.0
oss2==2.4.0 oss2==2.4.0
paramiko==2.4.1 paramiko==2.4.1
...@@ -42,3 +42,5 @@ urllib3==1.22 ...@@ -42,3 +42,5 @@ urllib3==1.22
wcwidth==0.1.7 wcwidth==0.1.7
eventlet==0.24.1 eventlet==0.24.1
Werkzeug==0.14.1 Werkzeug==0.14.1
treelib==1.5.3
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