Commit 6e153a1a authored by ibuler's avatar ibuler

[Update] youhua

parents 9d64f6c2 9c24dcb0
...@@ -20,7 +20,7 @@ from .session import Session ...@@ -20,7 +20,7 @@ from .session import Session
from .models import Connection from .models import Connection
__version__ = '1.4.4' __version__ = '1.4.6'
BASE_DIR = os.path.dirname(os.path.dirname(__file__)) BASE_DIR = os.path.dirname(os.path.dirname(__file__))
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -56,6 +56,7 @@ class Coco: ...@@ -56,6 +56,7 @@ class Coco:
return self._task_handler return self._task_handler
@staticmethod @staticmethod
@ignore_error
def load_extra_conf_from_server(): def load_extra_conf_from_server():
configs = app_service.load_config_from_server() configs = app_service.load_config_from_server()
logger.debug("Loading config from server: {}".format( logger.debug("Loading config from server: {}".format(
...@@ -63,8 +64,17 @@ class Coco: ...@@ -63,8 +64,17 @@ class Coco:
)) ))
config.update(configs) config.update(configs)
def keep_load_extra_conf(self):
def func():
while True:
self.load_extra_conf_from_server()
time.sleep(60*10)
thread = threading.Thread(target=func)
thread.start()
def bootstrap(self): def bootstrap(self):
self.load_extra_conf_from_server() self.load_extra_conf_from_server()
self.keep_load_extra_conf()
self.keep_heartbeat() self.keep_heartbeat()
self.monitor_sessions() self.monitor_sessions()
self.monitor_sessions_replay() self.monitor_sessions_replay()
......
...@@ -92,8 +92,9 @@ class Config(dict): ...@@ -92,8 +92,9 @@ class Config(dict):
""" """
def __init__(self, root_path, defaults=None): def __init__(self, root_path, defaults=None):
dict.__init__(self, defaults or {}) self.defaults = defaults or {}
self.root_path = root_path self.root_path = root_path
super().__init__({})
def from_envvar(self, variable_name, silent=False): def from_envvar(self, variable_name, silent=False):
"""Loads a configuration from an environment variable pointing to """Loads a configuration from an environment variable pointing to
...@@ -269,6 +270,21 @@ class Config(dict): ...@@ -269,6 +270,21 @@ class Config(dict):
rv[key] = v rv[key] = v
return rv return rv
def __getitem__(self, item):
try:
value = super().__getitem__(item)
except KeyError:
value = None
if value is not None:
return value
value = os.environ.get(item, None)
if value is not None:
return value
return self.defaults.get(item)
def __getattr__(self, item):
return self.__getitem__(item)
def __repr__(self): def __repr__(self):
return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
...@@ -277,6 +293,7 @@ access_key_path = os.path.abspath(os.path.join(root_path, 'keys', '.access_key') ...@@ -277,6 +293,7 @@ access_key_path = os.path.abspath(os.path.join(root_path, 'keys', '.access_key')
default_config = { default_config = {
'NAME': socket.gethostname(), 'NAME': socket.gethostname(),
'CORE_HOST': 'http://127.0.0.1:8080', 'CORE_HOST': 'http://127.0.0.1:8080',
'BOOTSTRAP_TOKEN': os.environ.get("BOOTSTRAP_TOKEN") or 'PleaseChangeMe',
'ROOT_PATH': root_path, 'ROOT_PATH': root_path,
'DEBUG': True, 'DEBUG': True,
'BIND_HOST': '0.0.0.0', 'BIND_HOST': '0.0.0.0',
...@@ -301,6 +318,7 @@ default_config = { ...@@ -301,6 +318,7 @@ default_config = {
'REPLAY_STORAGE': {'TYPE': 'server'}, 'REPLAY_STORAGE': {'TYPE': 'server'},
'LANGUAGE_CODE': 'zh', 'LANGUAGE_CODE': 'zh',
'SECURITY_MAX_IDLE_TIME': 60, 'SECURITY_MAX_IDLE_TIME': 60,
'ASSET_LIST_PAGE_SIZE': 'auto',
} }
config = Config(root_path, default_config) config = Config(root_path, default_config)
......
...@@ -41,12 +41,16 @@ class SSHConnection: ...@@ -41,12 +41,16 @@ class SSHConnection:
ssh = paramiko.SSHClient() ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
sock = None sock = None
error = ''
if not system_user.password and not system_user.private_key: if not system_user.password and not system_user.private_key:
self.get_system_user_auth(system_user) self.get_system_user_auth(system_user)
if asset.domain: if asset.domain:
sock = self.get_proxy_sock_v2(asset) sock = self.get_proxy_sock_v2(asset)
if not sock:
error = 'Connect gateway failed;'
logger.error(error)
try: try:
try: try:
...@@ -85,9 +89,9 @@ class SSHConnection: ...@@ -85,9 +89,9 @@ class SSHConnection:
system_user.username, asset.ip, asset.port, system_user.username, asset.ip, asset.port,
password_short, key_fingerprint, password_short, key_fingerprint,
)) ))
return None, None, str(e) return None, None, error + '\n' + str(e)
except (socket.error, socket.timeout) as e: except (socket.error, socket.timeout) as e:
return None, None, str(e) return None, None, error + '\n' + str(e)
return ssh, sock, None return ssh, sock, None
def get_transport(self, asset, system_user): def get_transport(self, asset, system_user):
...@@ -130,9 +134,9 @@ class SSHConnection: ...@@ -130,9 +134,9 @@ class SSHConnection:
password=gateway.password, password=gateway.password,
pkey=gateway.private_key_obj, pkey=gateway.private_key_obj,
timeout=config['SSH_TIMEOUT']) timeout=config['SSH_TIMEOUT'])
except(paramiko.AuthenticationException, except (paramiko.AuthenticationException,
paramiko.BadAuthenticationType, paramiko.BadAuthenticationType,
SSHException): SSHException, socket.error):
continue continue
sock = ssh.get_transport().open_channel( sock = ssh.get_transport().open_channel(
'direct-tcpip', (asset.ip, asset.port), ('127.0.0.1', 0) 'direct-tcpip', (asset.ip, asset.port), ('127.0.0.1', 0)
......
...@@ -136,6 +136,7 @@ class ElFinderConnector: ...@@ -136,6 +136,7 @@ class ElFinderConnector:
return cmd return cmd
else: else:
self.response['error'] = 'No valid command found' self.response['error'] = 'No valid command found'
return None, None
def run(self, request): def run(self, request):
""" Main entry point for running commands. Attemps to run a command """ Main entry point for running commands. Attemps to run a command
......
This diff is collapsed.
...@@ -176,7 +176,7 @@ class SSHInterface(paramiko.ServerInterface): ...@@ -176,7 +176,7 @@ class SSHInterface(paramiko.ServerInterface):
'command': command 'command': command
}) })
self.event.set() self.event.set()
return False return True
def check_channel_forward_agent_request(self, channel): def check_channel_forward_agent_request(self, channel):
logger.debug("Check channel forward agent request: %s" % channel) logger.debug("Check channel forward agent request: %s" % channel)
......
...@@ -10,7 +10,7 @@ from .config import config as app_config ...@@ -10,7 +10,7 @@ from .config import config as app_config
def create_logger(): def create_logger():
level = app_config['LOG_LEVEL'] level = app_config['LOG_LEVEL']
log_dir = app_config.get('LOG_DIR') log_dir = app_config['LOG_DIR']
log_path = os.path.join(log_dir, 'coco.log') log_path = os.path.join(log_dir, 'coco.log')
main_setting = { main_setting = {
'handlers': ['console', 'file'], 'handlers': ['console', 'file'],
......
...@@ -367,6 +367,11 @@ class TelnetServer(BaseServer): ...@@ -367,6 +367,11 @@ class TelnetServer(BaseServer):
self.system_user = system_user self.system_user = system_user
super(TelnetServer, self).__init__(chan=sock) super(TelnetServer, self).__init__(chan=sock)
@property
def closed(self):
""" self.chan: socket object """
return getattr(self.chan, '_closed', False)
class Server(BaseServer): class Server(BaseServer):
""" """
......
...@@ -52,7 +52,7 @@ class ProxyServer: ...@@ -52,7 +52,7 @@ class ProxyServer:
return False return False
return True return True
def manual_set_system_user_username_if_need(self): def get_system_user_username_if_need(self):
if self.system_user.login_mode == MANUAL_LOGIN and \ if self.system_user.login_mode == MANUAL_LOGIN and \
not self.system_user.username: not self.system_user.username:
username = net_input(self.client, prompt='username: ', before=1) username = net_input(self.client, prompt='username: ', before=1)
...@@ -63,13 +63,15 @@ class ProxyServer: ...@@ -63,13 +63,15 @@ class ProxyServer:
def proxy(self): def proxy(self):
if not self.check_protocol(): if not self.check_protocol():
return return
self.manual_set_system_user_username_if_need() self.get_system_user_username_if_need()
self.get_system_user_auth_or_manual_set() self.get_system_user_auth_or_manual_set()
self.server = self.get_server_conn() self.server = self.get_server_conn()
if self.server is None: if self.server is None:
return return
session = Session.new_session(self.client, self.server) session = Session.new_session(self.client, self.server)
try:
session.bridge() session.bridge()
finally:
Session.remove_session(session.id) Session.remove_session(session.id)
self.server.close() self.server.close()
......
...@@ -58,6 +58,7 @@ class SSHServer: ...@@ -58,6 +58,7 @@ class SSHServer:
logger.error("Start SSH server error: {}".format(e)) logger.error("Start SSH server error: {}".format(e))
def handle_connection(self, sock, addr): def handle_connection(self, sock, addr):
logger.debug("Handle new connection from: {}".format(addr))
transport = paramiko.Transport(sock, gss_kex=False) transport = paramiko.Transport(sock, gss_kex=False)
try: try:
transport.load_server_moduli() transport.load_server_moduli()
......
...@@ -303,7 +303,7 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0): ...@@ -303,7 +303,7 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0):
client.send(wrap_with_line_feed(prompt, before=before, after=after)) client.send(wrap_with_line_feed(prompt, before=before, after=after))
while True: while True:
data = client.recv(10) data = client.recv(1)
if len(data) == 0: if len(data) == 0:
break break
# Client input backspace # Client input backspace
...@@ -331,18 +331,8 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0): ...@@ -331,18 +331,8 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0):
client.send(b'') client.send(b'')
continue continue
# handle shell expect
multi_char_with_enter = False
if len(data) > 1 and data[-1] in char.ENTER_CHAR_ORDER:
if sensitive:
client.send(len(data) * '*')
else:
client.send(data)
input_data.append(data[:-1])
multi_char_with_enter = True
# If user types ENTER we should get user input # If user types ENTER we should get user input
if data in char.ENTER_CHAR or multi_char_with_enter: if data in char.ENTER_CHAR:
client.send(wrap_with_line_feed(b'', after=2)) client.send(wrap_with_line_feed(b'', after=2))
option = parser.parse_input(input_data) option = parser.parse_input(input_data)
del input_data[:] del input_data[:]
......
...@@ -17,6 +17,10 @@ class Config: ...@@ -17,6 +17,10 @@ class Config:
# Jumpserver项目的url, api请求注册会使用 # Jumpserver项目的url, api请求注册会使用
# CORE_HOST = os.environ.get("CORE_HOST") or 'http://127.0.0.1:8080' # CORE_HOST = os.environ.get("CORE_HOST") or 'http://127.0.0.1:8080'
# Bootstrap Token, 预共享秘钥, 用来注册coco使用的service account和terminal
# 请和jumpserver 配置文件中保持一致,注册完成后可以删除
# BOOTSTRAP_TOKEN = "PleaseChangeMe"
# 启动时绑定的ip, 默认 0.0.0.0 # 启动时绑定的ip, 默认 0.0.0.0
# BIND_HOST = '0.0.0.0' # BIND_HOST = '0.0.0.0'
......
...@@ -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-31 11:49+0800\n" "POT-Creation-Date: 2018-12-18 20:03+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:135 #: coco/app.py:145
msgid "Connect idle more than {} minutes, disconnect" msgid "Connect idle more than {} minutes, disconnect"
msgstr "" msgstr ""
#: coco/interactive.py:80 #: coco/interactive.py:84
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"\n" "\n"
...@@ -28,131 +28,127 @@ msgid "" ...@@ -28,131 +28,127 @@ msgid ""
"{end}{R}{R}" "{end}{R}{R}"
msgstr "" msgstr ""
#: coco/interactive.py:82 #: coco/interactive.py:86
#, 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:83 #: coco/interactive.py:87
#, 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:84 #: coco/interactive.py:88
#, 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:85 #: coco/interactive.py:89
#, 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:86 #: coco/interactive.py:90
#, python-brace-format #, python-brace-format
msgid "" msgid ""
"{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the " "{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the "
"node, such as g1.{R}" "node, such as g1.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:87 #: coco/interactive.py:91
#, 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:88 #: coco/interactive.py:92
#, 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:89 #: coco/interactive.py:93
#, python-brace-format #, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}" msgid "{T}8) Enter {green}r{end} to refresh your assets and nodes.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:159 #: coco/interactive.py:94
msgid "No" #, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "" msgstr ""
#: coco/interactive.py:166 #: coco/interactive.py:155
msgid "Name" msgid "Terminal does not support login rdp, please use web terminal to access"
msgstr "" msgstr ""
#: coco/interactive.py:166 #: coco/interactive.py:212
msgid "Assets" msgid "No Assets"
msgstr "" msgstr ""
#: coco/interactive.py:172 #: coco/interactive.py:275
msgid "Total: {}" msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "" msgstr ""
#: coco/interactive.py:177 #: coco/interactive.py:276
msgid "Node [ ID.Name(Asset) ]" msgid "Page up: P/p"
msgstr "" msgstr ""
#: coco/interactive.py:179 #: coco/interactive.py:277
msgid "Enter g+NodeID to display the host under the node, such as g1." msgid "Page down: Enter|N/n"
msgstr "" msgstr ""
#: coco/interactive.py:186 #: coco/interactive.py:278
msgid "There is no matched node, please re-enter" msgid "BACK: b/q"
msgstr "" msgstr ""
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "Hostname" msgid "Hostname"
msgstr "" msgstr ""
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "IP" msgid "IP"
msgstr "" msgstr ""
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "LoginAs" msgid "LoginAs"
msgstr "" msgstr ""
#: coco/interactive.py:211 #: coco/interactive.py:313
msgid "Comment" msgid "Comment"
msgstr "" msgstr ""
#: coco/interactive.py:221 #: coco/interactive.py:322
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}" msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "" msgstr ""
#: coco/interactive.py:296 #: coco/interactive.py:394
msgid "Select a login:: " msgid "No Nodes"
msgstr "" msgstr ""
#: coco/interactive.py:319 #: coco/interactive.py:398
msgid "" msgid "Node: [ ID.Name(Asset amount) ]"
"Terminal does not support login Windows, please use web terminal to access"
msgstr "" msgstr ""
#: coco/interactive.py:401 #: coco/interactive.py:400
msgid "Tips: Enter the asset ID and log directly into the asset." msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr "" msgstr ""
#: coco/interactive.py:402 #: coco/interactive.py:408
msgid "Page up: P/p" msgid "There is no matched node, please re-enter"
msgstr ""
#: coco/interactive.py:403
msgid "Page down: Enter|N/n"
msgstr "" msgstr ""
#: coco/interactive.py:404 #: coco/interactive.py:438
msgid "BACK: B/b" msgid "Select a login:: "
msgstr "" msgstr ""
#: coco/interactive.py:425 #: coco/interactive.py:461
msgid "No system user" msgid "No system user"
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-31 11:49+0800\n" "POT-Creation-Date: 2018-12-18 20:04+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:135 #: coco/app.py:145
msgid "Connect idle more than {} minutes, disconnect" msgid "Connect idle more than {} minutes, disconnect"
msgstr "空闲时间超过 {} 分钟,断开连接" msgstr "空闲时间超过 {} 分钟,断开连接"
#: coco/interactive.py:80 #: coco/interactive.py:84
#, 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:82 #: coco/interactive.py:86
#, 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:83 #: coco/interactive.py:87
#, 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,122 +48,120 @@ msgstr "" ...@@ -48,122 +48,120 @@ 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:84 #: coco/interactive.py:88
#, 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:85 #: coco/interactive.py:89
#, 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:86 #: coco/interactive.py:90
#, python-brace-format
msgid "" msgid ""
"{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under the " "{T}5) Enter {green}g{end} + {green}NodeID{end} to display the host under 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:87 #: coco/interactive.py:91
#, 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:88 #: coco/interactive.py:92
#, 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:89 #: coco/interactive.py:93
#, python-brace-format
msgid "{T}8) Enter {green}r{end} to refresh your assets and nodes.{R}"
msgstr "{T}0) 输入 {green}r{end} 刷新最新的机器和节点信息.{R}"
#: coco/interactive.py:94
#, 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:159 #: coco/interactive.py:155
msgid "No" msgid "Terminal does not support login rdp, please use web terminal to access"
msgstr "无" msgstr "终端不支持登录windows, 请使用web terminal访问"
#: coco/interactive.py:166
msgid "Name"
msgstr "名称"
#: coco/interactive.py:166 #: coco/interactive.py:212
msgid "Assets" msgid "No Assets"
msgstr "资产" msgstr "没有资产"
#: coco/interactive.py:172 #: coco/interactive.py:275
msgid "Total: {}" msgid "Tips: Enter the asset ID and log directly into the asset."
msgstr "总共: {}" msgstr "提示: 输入资产ID,直接登录资产."
#: coco/interactive.py:177 #: coco/interactive.py:276
msgid "Node: [ ID.Name(Asset amount) ]" msgid "Page up: P/p"
msgstr "节点: [ ID.名称(资产数量) ]" msgstr "上一页: P/p"
#: coco/interactive.py:179 #: coco/interactive.py:277
msgid "Tips: Enter g+NodeID to display the host under the node, such as g1" msgid "Page down: Enter|N/n"
msgstr "提示: 输入 g+节点ID 显示节点下主机. 如: g1" msgstr "下一页: Enter|N/n"
#: coco/interactive.py:186 #: coco/interactive.py:278
msgid "There is no matched node, please re-enter" msgid "BACK: b/q"
msgstr "没有匹配分组,请重新输入" msgstr "返回: B/b"
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "ID" msgid "ID"
msgstr "" msgstr ""
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "Hostname" msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "IP" msgid "IP"
msgstr "" msgstr ""
#: coco/interactive.py:197 #: coco/interactive.py:299
msgid "LoginAs" msgid "LoginAs"
msgstr "登录用户" msgstr "登录用户"
#: coco/interactive.py:211 #: coco/interactive.py:313
msgid "Comment" msgid "Comment"
msgstr "备注" msgstr "备注"
#: coco/interactive.py:221 #: coco/interactive.py:322
msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}" msgid "Page: {}, Count: {}, Total Page: {}, Total Count: {}"
msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}" msgstr "页码: {}, 数量: {}, 总页数: {}, 总数量: {}"
#: coco/interactive.py:296 #: coco/interactive.py:394
msgid "Select a login:: " msgid "No Nodes"
msgstr "选择一个登录:" msgstr "没有节点"
#: coco/interactive.py:319
msgid ""
"Terminal does not support login Windows, please use web terminal to access"
msgstr "终端不支持登录windows, 请使用web terminal访问"
#: coco/interactive.py:401 #: coco/interactive.py:398
msgid "Tips: Enter the asset ID and log directly into the asset." msgid "Node: [ ID.Name(Asset amount) ]"
msgstr "提示: 输入资产ID,直接登录资产." msgstr "节点: [ ID.名称(资产数量) ]"
#: coco/interactive.py:402 #: coco/interactive.py:400
msgid "Page up: P/p" msgid "Tips: Enter g+NodeID to display the host under the node, such as g1"
msgstr "上一页: P/p" msgstr "提示: 输入 g+节点ID 显示节点下主机. 如: g1"
#: coco/interactive.py:403 #: coco/interactive.py:408
msgid "Page down: Enter|N/n" msgid "There is no matched node, please re-enter"
msgstr "下一页: Enter|N/n" msgstr "没有匹配分组,请重新输入"
#: coco/interactive.py:404 #: coco/interactive.py:438
msgid "BACK: B/b" msgid "Select a login:: "
msgstr "返回: B/b" msgstr "选择一个登录:"
#: coco/interactive.py:425 #: coco/interactive.py:461
msgid "No system user" msgid "No system user"
msgstr "没有系统用户" msgstr "没有系统用户"
#: coco/models.py:247 #: coco/models.py:247
msgid "Command `{}` is forbidden ........" msgid "Command `{}` is forbidden ........"
msgstr "" msgstr "命令 `{}` 是被禁止的 ..."
#: coco/proxy.py:89 #: coco/proxy.py:89
msgid "No permission" msgid "No permission"
...@@ -177,5 +175,14 @@ msgstr "开始连接到 {}@{} {:.1f}" ...@@ -177,5 +175,14 @@ msgstr "开始连接到 {}@{} {:.1f}"
msgid "Terminated by administrator" msgid "Terminated by administrator"
msgstr "被管理员中断" msgstr "被管理员中断"
#~ msgid "No"
#~ msgstr "无"
#~ msgid "Name"
#~ msgstr "名称"
#~ msgid "Total: {}"
#~ msgstr "总共: {}"
#~ msgid "Total: {} Match: {}" #~ msgid "Total: {} Match: {}"
#~ msgstr "总共: {} 匹配: {}" #~ msgstr "总共: {} 匹配: {}"
krb5-dev sshpass libffi-dev libressl-dev linux-headers
...@@ -19,7 +19,7 @@ itsdangerous==0.24 ...@@ -19,7 +19,7 @@ itsdangerous==0.24
Jinja2==2.10 Jinja2==2.10
jmespath==0.9.3 jmespath==0.9.3
jms-storage==0.0.20 jms-storage==0.0.20
jumpserver-python-sdk==0.0.51 jumpserver-python-sdk==0.0.53
MarkupSafe==1.0 MarkupSafe==1.0
oss2==2.4.0 oss2==2.4.0
paramiko==2.4.1 paramiko==2.4.1
......
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