Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
C
coco
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
coco
Commits
5490dff8
Commit
5490dff8
authored
Nov 05, 2018
by
ibuler
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'github/dev' into dev
parents
34b7e6b9
0e1de290
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
315 additions
and
97 deletions
+315
-97
interactive.py
coco/interactive.py
+194
-34
coco.mo
locale/en/LC_MESSAGES/coco.mo
+0
-0
coco.po
locale/en/LC_MESSAGES/coco.po
+58
-30
coco.mo
locale/zh_CN/LC_MESSAGES/coco.mo
+0
-0
coco.po
locale/zh_CN/LC_MESSAGES/coco.po
+63
-33
No files found.
coco/interactive.py
View file @
5490dff8
...
...
@@ -5,6 +5,9 @@
import
socket
import
threading
import
os
import
math
import
time
from
treelib
import
Tree
from
.
import
char
from
.config
import
config
...
...
@@ -17,19 +20,35 @@ from .proxy import ProxyServer
logger
=
get_logger
(
__file__
)
PAGE_DOWN
=
'down'
PAGE_UP
=
'up'
BACK
=
'back'
PROXY
=
'proxy'
class
InteractiveServer
:
_sentinel
=
object
()
def
__init__
(
self
,
client
):
self
.
client
=
client
self
.
assets
=
None
self
.
closed
=
False
self
.
_search_result
=
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
()
@property
def
page_size
(
self
):
return
self
.
client
.
request
.
meta
[
'height'
]
-
8
@property
def
search_result
(
self
):
if
self
.
_search_result
:
...
...
@@ -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}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}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}
Node
ID{end} to display the host under the node, such as g1.{R}"
),
_
(
"{T}6) Enter {green}s{end} Chinese-english switch.{R}"
),
_
(
"{T}7) Enter {green}h{end} help.{R}"
),
_
(
"{T}0) Enter {green}q{end} exit.{R}"
)
...
...
@@ -87,7 +106,7 @@ class InteractiveServer:
elif
opt
in
[
'p'
,
'P'
,
''
]:
self
.
display_assets
()
elif
opt
in
[
'g'
,
'G'
]:
self
.
display_nodes
()
self
.
display_nodes
_tree
()
elif
opt
.
startswith
(
"g"
)
and
opt
.
lstrip
(
"g"
)
.
isdigit
():
self
.
display_node_assets
(
int
(
opt
.
lstrip
(
"g"
)))
elif
opt
in
[
'q'
,
'Q'
,
'exit'
,
'quit'
]:
...
...
@@ -101,38 +120,36 @@ class InteractiveServer:
self
.
search_and_proxy
(
opt
)
def
search_assets
(
self
,
q
):
if
self
.
assets
is
None
:
self
.
get_user_assets
()
if
not
self
.
finish
:
assets
=
app_service
.
get_search_user_granted_assets
(
self
.
client
.
user
,
q
)
return
assets
assets
=
self
.
assets_list
result
=
[]
# 所有的
if
q
in
(
''
,
None
):
result
=
self
.
assets
# 用户输入的是数字,可能想使用id唯一键搜索
elif
q
.
isdigit
()
and
self
.
search_result
and
\
len
(
self
.
search_result
)
>=
int
(
q
):
result
=
[
self
.
search_result
[
int
(
q
)
-
1
]]
result
=
assets
# 全匹配到则直接返回全匹配的
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
len
(
_result
)
==
1
:
result
=
_result
# 最后模糊匹配
if
len
(
result
)
==
0
:
result
=
[
asset
for
asset
in
self
.
assets
result
=
[
asset
for
asset
in
assets
if
is_obj_attr_has
(
asset
,
q
)]
self
.
search_result
=
result
return
result
def
display_assets
(
self
):
"""
Display user all assets
:return:
"""
self
.
search_and_display
(
''
)
self
.
display_result_paging
(
self
.
assets_list
)
def
display_nodes
(
self
):
if
self
.
nodes
is
None
:
...
...
@@ -154,17 +171,31 @@ class InteractiveServer:
self
.
client
.
send
(
wr
(
format_with_zh
(
size_list
,
*
data
)))
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
):
if
self
.
nodes
is
None
:
self
.
get_user_nodes
()
if
_id
>
len
(
self
.
nodes
)
or
_id
<=
0
:
msg
=
wr
(
warning
(
_
(
"There is no matched node, please re-enter"
)))
self
.
client
.
send
(
msg
)
self
.
display_nodes
()
self
.
display_nodes
_tree
()
return
self
.
search_result
=
self
.
nodes
[
_id
-
1
]
.
assets_granted
self
.
display_
search_result
(
)
assets
=
self
.
nodes
[
_id
-
1
]
.
assets_granted
self
.
display_
result_paging
(
assets
)
def
display_search_result
(
self
):
sort_by
=
config
[
"ASSET_LIST_SORT_BY"
]
...
...
@@ -191,16 +222,31 @@ class InteractiveServer:
asset
.
system_users_name_list
,
asset
.
comment
]
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
):
self
.
search_assets
(
q
)
self
.
display_
search_result
(
)
assets
=
self
.
search_assets
(
q
)
self
.
display_
result_paging
(
assets
)
def
get_user_nodes
(
self
):
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
):
thread
=
threading
.
Thread
(
target
=
self
.
get_user_nodes
)
...
...
@@ -217,14 +263,24 @@ class InteractiveServer:
asset
.
system_users_granted
=
system_users_cleaned
return
assets
def
get_user_assets
(
self
):
self
.
assets
=
app_service
.
get_user_assets
(
self
.
client
.
user
)
logger
.
debug
(
"Get user {} assets total: {}"
.
format
(
self
.
client
.
user
,
len
(
self
.
assets
))
)
def
get_user_assets_async
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets
)
def
get_user_assets_paging
(
self
):
while
not
self
.
closed
:
assets
,
total
=
app_service
.
get_user_assets_paging
(
self
.
client
.
user
,
offset
=
self
.
offset
,
limit
=
self
.
limit
)
logger
.
info
(
'Get user assets paging async: {}'
.
format
(
len
(
assets
)))
if
not
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
()
def
choose_system_user
(
self
,
system_users
):
...
...
@@ -251,9 +307,9 @@ class InteractiveServer:
self
.
client
.
send
(
wr
(
"{} {}"
.
format
(
index
,
system_user
.
name
)))
def
search_and_proxy
(
self
,
opt
):
self
.
search_assets
(
opt
)
if
self
.
search_result
and
len
(
self
.
search_result
)
==
1
:
asset
=
self
.
search_result
[
0
]
assets
=
self
.
search_assets
(
opt
)
if
assets
and
len
(
assets
)
==
1
:
asset
=
assets
[
0
]
self
.
search_result
=
None
if
asset
.
platform
==
"Windows"
:
self
.
client
.
send
(
warning
(
...
...
@@ -263,7 +319,111 @@ class InteractiveServer:
return
self
.
proxy
(
asset
)
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/b"
)
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'
,
'b'
):
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
):
system_user
=
self
.
choose_system_user
(
asset
.
system_users_granted
)
...
...
locale/en/LC_MESSAGES/coco.mo
View file @
5490dff8
No preview for this file type
locale/en/LC_MESSAGES/coco.po
View file @
5490dff8
...
...
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/en/LC\n"
...
...
@@ -16,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: coco/app.py:1
41
#: coco/app.py:1
35
msgid "Connect idle more than {} minutes, disconnect"
msgstr ""
#: coco/interactive.py:
61
#: coco/interactive.py:
80
#, python-brace-format
msgid ""
"\n"
...
...
@@ -28,115 +28,143 @@ msgid ""
"{end}{R}{R}"
msgstr ""
#: coco/interactive.py:
63
#: coco/interactive.py:
82
#, python-brace-format
msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
"Comment{end} to search login(if unique).{R}"
msgstr ""
#: coco/interactive.py:
64
#: coco/interactive.py:
83
#, python-brace-format
msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
"search, such as: /ip.{R}"
msgstr ""
#: coco/interactive.py:
65
#: coco/interactive.py:
84
#, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr ""
#: coco/interactive.py:
66
#: coco/interactive.py:
85
#, python-brace-format
msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr ""
#: coco/interactive.py:
67
#: coco/interactive.py:
86
#, python-brace-format
msgid ""
"{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}"
msgstr ""
#: coco/interactive.py:
68
#: coco/interactive.py:
87
#, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr ""
#: coco/interactive.py:
69
#: coco/interactive.py:
88
#, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr ""
#: coco/interactive.py:
70
#: coco/interactive.py:
89
#, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr ""
#: coco/interactive.py:1
42
#: coco/interactive.py:1
59
msgid "No"
msgstr ""
#: coco/interactive.py:1
49
#: coco/interactive.py:1
66
msgid "Name"
msgstr ""
#: coco/interactive.py:1
49
#: coco/interactive.py:1
66
msgid "Assets"
msgstr ""
#: coco/interactive.py:1
55
#: coco/interactive.py:1
72
msgid "Total: {}"
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"
msgstr ""
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "ID"
msgstr ""
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "Hostname"
msgstr ""
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "IP"
msgstr ""
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "LoginAs"
msgstr ""
#: coco/interactive.py:
186
#: coco/interactive.py:
211
msgid "Comment"
msgstr ""
#: coco/interactive.py:
194
msgid "
Total: {} Match
: {}"
#: coco/interactive.py:
221
msgid "
Page: {}, Count: {}, Total Page: {}, Total Count
: {}"
msgstr ""
#: coco/interactive.py:2
37
#: coco/interactive.py:2
96
msgid "Select a login:: "
msgstr ""
#: coco/interactive.py:
260
#: coco/interactive.py:
319
msgid ""
"Terminal does not support login Windows, please use web terminal to access"
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"
msgstr ""
#: coco/proxy.py:88
#: coco/models.py:247
msgid "Command `{}` is forbidden ........"
msgstr ""
#: coco/proxy.py:89
msgid "No permission"
msgstr ""
#: coco/proxy.py:13
0
#: coco/proxy.py:13
1
msgid "Connecting to {}@{} {:.1f}"
msgstr ""
...
...
locale/zh_CN/LC_MESSAGES/coco.mo
View file @
5490dff8
No preview for this file type
locale/zh_CN/LC_MESSAGES/coco.po
View file @
5490dff8
...
...
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\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"
"Last-Translator: BaiJiangjie <bugatti_it@163.com>\n"
"Language-Team: Language locale/zh\n"
...
...
@@ -16,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: coco/app.py:1
41
#: coco/app.py:1
35
msgid "Connect idle more than {} minutes, disconnect"
msgstr "空闲时间超过 {} 分钟,断开连接"
#: coco/interactive.py:
61
#: coco/interactive.py:
80
#, python-brace-format
msgid ""
"\n"
...
...
@@ -30,7 +30,7 @@ msgstr ""
"\n"
"{T}{T}{title} {user}, 欢迎使用Jumpserver开源跳板机系统 {end}{R}{R}"
#: coco/interactive.py:
63
#: coco/interactive.py:
82
#, python-brace-format
msgid ""
"{T}1) Enter {green}ID{end} directly login or enter {green}part IP, Hostname, "
...
...
@@ -39,7 +39,7 @@ msgstr ""
"{T}1) 输入 {green}ID{end} 直接登录 或 输入{green}部分 IP,主机名,备注{end} 进"
"行搜索登录(如果唯一).{R}"
#: coco/interactive.py:
64
#: coco/interactive.py:
83
#, python-brace-format
msgid ""
"{T}2) Enter {green}/{end} + {green}IP, Hostname{end} or {green}Comment {end} "
...
...
@@ -48,104 +48,134 @@ msgstr ""
"{T}2) 输入 {green}/{end} + {green}IP, 主机名{end} or {green}备注 {end}搜索. "
"如: /ip{R}"
#: coco/interactive.py:
65
#: coco/interactive.py:
84
#, python-brace-format
msgid "{T}3) Enter {green}p{end} to display the host you have permission.{R}"
msgstr "{T}3) 输入 {green}p{end} 显示您有权限的主机.{R}"
#: coco/interactive.py:
66
#: coco/interactive.py:
85
#, python-brace-format
msgid ""
"{T}4) Enter {green}g{end} to display the node that you have permission.{R}"
msgstr "{T}4) 输入 {green}g{end} 显示您有权限的节点.{R}"
#: coco/interactive.py:67
#, python-brace-format
#: coco/interactive.py:86
msgid ""
"{T}5) Enter {green}g{end} + {green}
Group ID{end} to display the host under
"
"
the
node, such as g1.{R}"
msgstr "{T}5) 输入 {green}g{end} + {green}
组
ID{end} 显示节点下主机. 如: g1{R}"
"{T}5) Enter {green}g{end} + {green}
NodeID{end} to display the host under the
"
"node, such as g1.{R}"
msgstr "{T}5) 输入 {green}g{end} + {green}
节点
ID{end} 显示节点下主机. 如: g1{R}"
#: coco/interactive.py:
68
#: coco/interactive.py:
87
#, python-brace-format
msgid "{T}6) Enter {green}s{end} Chinese-english switch.{R}"
msgstr "{T}6) 输入 {green}s{end} 中/英文切换.{R}"
#: coco/interactive.py:
69
#: coco/interactive.py:
88
#, python-brace-format
msgid "{T}7) Enter {green}h{end} help.{R}"
msgstr "{T}7) 输入 {green}h{end} 帮助.{R}"
#: coco/interactive.py:
70
#: coco/interactive.py:
89
#, python-brace-format
msgid "{T}0) Enter {green}q{end} exit.{R}"
msgstr "{T}0) 输入 {green}q{end} 退出.{R}"
#: coco/interactive.py:1
42
#: coco/interactive.py:1
59
msgid "No"
msgstr "无"
#: coco/interactive.py:1
49
#: coco/interactive.py:1
66
msgid "Name"
msgstr "名称"
#: coco/interactive.py:1
49
#: coco/interactive.py:1
66
msgid "Assets"
msgstr "资产"
#: coco/interactive.py:1
55
#: coco/interactive.py:1
72
msgid "Total: {}"
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"
msgstr "没有匹配分组,请重新输入"
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "ID"
msgstr ""
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "Hostname"
msgstr "主机名"
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "IP"
msgstr ""
#: coco/interactive.py:1
72
#: coco/interactive.py:1
97
msgid "LoginAs"
msgstr "登录用户"
#: coco/interactive.py:
186
#: coco/interactive.py:
211
msgid "Comment"
msgstr "备注"
#: coco/interactive.py:
194
msgid "
Total: {} Match
: {}"
msgstr "
总共: {} 匹配
: {}"
#: coco/interactive.py:
221
msgid "
Page: {}, Count: {}, Total Page: {}, Total Count
: {}"
msgstr "
页码: {}, 数量: {}, 总页数: {}, 总数量
: {}"
#: coco/interactive.py:2
37
#: coco/interactive.py:2
96
msgid "Select a login:: "
msgstr "选择一个登录:"
#: coco/interactive.py:
260
#: coco/interactive.py:
319
msgid ""
"Terminal does not support login Windows, please use web terminal to access"
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"
msgstr "没有系统用户"
#: coco/proxy.py:88
#: coco/models.py:247
msgid "Command `{}` is forbidden ........"
msgstr ""
#: coco/proxy.py:89
msgid "No permission"
msgstr "没有权限"
#: coco/proxy.py:13
0
#: coco/proxy.py:13
1
msgid "Connecting to {}@{} {:.1f}"
msgstr "开始连接到 {}@{} {:.1f}"
#: coco/session.py:143
msgid "Terminated by administrator"
msgstr "被管理员中断"
#~ msgid "Total: {} Match: {}"
#~ msgstr "总共: {} 匹配: {}"
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment