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
6f4273c6
Unverified
Commit
6f4273c6
authored
Nov 11, 2018
by
老广
Committed by
GitHub
Nov 11, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #124 from jumpserver/dev
Dev
parents
5f8d1227
88a9e2d3
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
320 additions
and
101 deletions
+320
-101
connection.py
coco/connection.py
+3
-3
connector.py
coco/httpd/elfinder/connector.py
+0
-1
interactive.py
coco/interactive.py
+192
-32
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
requirements.txt
requirements/requirements.txt
+4
-2
No files found.
coco/connection.py
View file @
6f4273c6
...
...
@@ -183,13 +183,13 @@ class TelnetConnection:
r'incorrect|failed|失败|错误'
,
re
.
I
)
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
(
r'
password:\s*$|passwd:\s*$|密\s*码:
\s*$'
,
re
.
I
r'
Password:?\s*$|passwd:?\s*$|密\s*码:?
\s*$'
,
re
.
I
)
self
.
success_pattern
=
re
.
compile
(
r'Last\s*login|success|成功'
,
re
.
I
r'Last\s*login|success|成功
|#|\$
'
,
re
.
I
)
def
get_socket
(
self
):
...
...
coco/httpd/elfinder/connector.py
View file @
6f4273c6
...
...
@@ -40,7 +40,6 @@ class ElFinderConnector:
'uplMaxSize'
:
'10M'
,
'options'
:
{
'separator'
:
'/'
,
'disabled'
:
[],
'archivers'
:
{
'create'
:
[],
'extract'
:
[]
...
...
coco/interactive.py
View file @
6f4273c6
...
...
@@ -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
_paging
(
self
):
while
not
self
.
closed
:
assets
,
total
=
app_service
.
get_user_assets_paging
(
self
.
client
.
user
,
offset
=
self
.
offset
,
limit
=
self
.
limit
)
def
get_user_assets_async
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets
)
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_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
):
system_user
=
self
.
choose_system_user
(
asset
.
system_users_granted
)
...
...
locale/en/LC_MESSAGES/coco.mo
View file @
6f4273c6
No preview for this file type
locale/en/LC_MESSAGES/coco.po
View file @
6f4273c6
...
...
@@ -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 @
6f4273c6
No preview for this file type
locale/zh_CN/LC_MESSAGES/coco.po
View file @
6f4273c6
...
...
@@ -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 "总共: {} 匹配: {}"
requirements/requirements.txt
View file @
6f4273c6
...
...
@@ -18,8 +18,8 @@ idna==2.6
itsdangerous==0.24
Jinja2==2.10
jmespath==0.9.3
jms-storage==0.0.
19
jumpserver-python-sdk==0.0.5
0
jms-storage==0.0.
20
jumpserver-python-sdk==0.0.5
1
MarkupSafe==1.0
oss2==2.4.0
paramiko==2.4.1
...
...
@@ -42,3 +42,5 @@ urllib3==1.22
wcwidth==0.1.7
eventlet==0.24.1
Werkzeug==0.14.1
treelib==1.5.3
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