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
2983f06a
Unverified
Commit
2983f06a
authored
Dec 19, 2018
by
老广
Committed by
GitHub
Dec 19, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #145 from jumpserver/dev
Dev
parents
1233b998
f510d762
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
420 additions
and
365 deletions
+420
-365
app.py
coco/app.py
+10
-0
config.py
coco/config.py
+18
-1
interactive.py
coco/interactive.py
+273
-248
logger.py
coco/logger.py
+1
-1
models.py
coco/models.py
+5
-0
sshd.py
coco/sshd.py
+2
-1
utils.py
coco/utils.py
+5
-1
coco.mo
locale/en/LC_MESSAGES/coco.mo
+0
-0
coco.po
locale/en/LC_MESSAGES/coco.po
+41
-50
coco.mo
locale/zh_CN/LC_MESSAGES/coco.mo
+0
-0
coco.po
locale/zh_CN/LC_MESSAGES/coco.po
+65
-63
No files found.
coco/app.py
View file @
2983f06a
...
@@ -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
()
...
...
coco/config.py
View file @
2983f06a
...
@@ -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
))
...
@@ -302,6 +318,7 @@ default_config = {
...
@@ -302,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
)
...
...
coco/interactive.py
View file @
2983f06a
...
@@ -32,47 +32,51 @@ class InteractiveServer:
...
@@ -32,47 +32,51 @@ class InteractiveServer:
def
__init__
(
self
,
client
):
def
__init__
(
self
,
client
):
self
.
client
=
client
self
.
client
=
client
self
.
closed
=
False
self
.
closed
=
False
self
.
_
search_result
=
None
self
.
_
results
=
None
self
.
nodes
=
None
self
.
nodes
=
None
self
.
offset
=
0
self
.
offset
=
0
self
.
limit
=
100
self
.
limit
=
100
self
.
assets
_list
=
[]
self
.
assets
=
[]
self
.
finish
=
False
self
.
finish
=
False
self
.
page
=
1
self
.
page
=
1
self
.
total_assets
=
0
self
.
total_assets
=
0
# 用户被授权的所有资产
self
.
total_count
=
0
# 分页展示中
用来存放数目总条数
self
.
total_count
=
0
# 分页展示中
的资产总数量
self
.
node
s
_tree
=
None
# 授权节点树
self
.
node_tree
=
None
# 授权节点树
self
.
get_user_assets_
paging_
async
()
self
.
get_user_assets_async
()
self
.
get_user_nodes_async
()
self
.
get_user_nodes_async
()
@property
@property
def
page_size
(
self
):
def
page_size
(
self
):
return
self
.
client
.
request
.
meta
[
'height'
]
-
8
_page_size
=
config
[
'ASSET_LIST_PAGE_SIZE'
]
if
_page_size
.
isdigit
():
return
int
(
_page_size
)
elif
_page_size
==
'all'
:
return
self
.
total_count
else
:
return
self
.
client
.
request
.
meta
[
'height'
]
-
8
@property
def
total_pages
(
self
):
return
math
.
ceil
(
self
.
total_count
/
self
.
page_size
)
@property
def
need_paging
(
self
):
return
config
[
'ASSET_LIST_PAGE_SIZE'
]
!=
'all'
@property
@property
def
search_result
(
self
):
def
results
(
self
):
if
self
.
_
search_result
:
if
self
.
_
results
:
return
self
.
_
search_result
return
self
.
_
results
else
:
else
:
return
[]
return
[]
@search_result.setter
@results.setter
def
search_result
(
self
,
value
):
def
results
(
self
,
value
):
if
not
value
:
self
.
_results
=
value
self
.
_search_result
=
value
return
value
=
self
.
filter_system_users
(
value
)
self
.
_search_result
=
value
def
display_logo
(
self
):
#
logo_path
=
os
.
path
.
join
(
config
[
'ROOT_PATH'
],
"logo.txt"
)
# Display banner
if
not
os
.
path
.
isfile
(
logo_path
):
#
return
with
open
(
logo_path
,
'rb'
)
as
f
:
for
i
in
f
:
if
i
.
decode
(
'utf-8'
)
.
startswith
(
'#'
):
continue
self
.
client
.
send
(
i
.
decode
(
'utf-8'
)
.
replace
(
'
\n
'
,
'
\r\n
'
))
def
display_banner
(
self
):
def
display_banner
(
self
):
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
...
@@ -99,15 +103,25 @@ class InteractiveServer:
...
@@ -99,15 +103,25 @@ class InteractiveServer:
T
=
'
\t
'
,
R
=
'
\r\n\r
'
T
=
'
\t
'
,
R
=
'
\r\n\r
'
))
))
def
display_logo
(
self
):
logo_path
=
os
.
path
.
join
(
config
[
'ROOT_PATH'
],
"logo.txt"
)
if
not
os
.
path
.
isfile
(
logo_path
):
return
with
open
(
logo_path
,
'rb'
)
as
f
:
for
i
in
f
:
if
i
.
decode
(
'utf-8'
)
.
startswith
(
'#'
):
continue
self
.
client
.
send
(
i
.
decode
(
'utf-8'
)
.
replace
(
'
\n
'
,
'
\r\n
'
))
def
dispatch
(
self
,
opt
):
def
dispatch
(
self
,
opt
):
if
opt
is
None
:
if
opt
is
None
:
return
self
.
_sentinel
return
self
.
_sentinel
elif
opt
.
startswith
(
"/"
):
elif
opt
.
startswith
(
"/"
):
self
.
search_and_display
(
opt
.
lstrip
(
"/"
))
self
.
search_and_display
_assets
(
opt
.
lstrip
(
"/"
))
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_tree
()
self
.
display_nodes_
as_
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'
]:
...
@@ -121,98 +135,172 @@ class InteractiveServer:
...
@@ -121,98 +135,172 @@ class InteractiveServer:
elif
opt
in
[
'h'
,
'H'
]:
elif
opt
in
[
'h'
,
'H'
]:
self
.
display_banner
()
self
.
display_banner
()
else
:
else
:
self
.
search_and_proxy
(
opt
)
self
.
search_and_proxy_assets
(
opt
)
#
# Search assets
#
def
search_and_display_assets
(
self
,
q
):
assets
=
self
.
search_assets
(
q
)
self
.
display_assets
(
assets
)
def
search_and_proxy_assets
(
self
,
opt
):
assets
=
self
.
search_assets
(
opt
)
if
assets
and
len
(
assets
)
==
1
:
asset
=
assets
[
0
]
if
asset
.
protocol
==
"rdp"
\
or
asset
.
platform
.
lower
()
.
startswith
(
"windows"
):
self
.
client
.
send
(
warning
(
_
(
"Terminal does not support login rdp, "
"please use web terminal to access"
))
)
return
self
.
proxy
(
asset
)
else
:
self
.
display_assets
(
assets
)
def
refresh_assets_nodes
(
self
):
def
refresh_assets_nodes
(
self
):
self
.
get_user_assets_
paging_
async
()
self
.
get_user_assets_async
()
self
.
get_user_nodes_async
()
self
.
get_user_nodes_async
()
def
search_assets
(
self
,
q
):
def
search_assets
(
self
,
q
):
if
not
self
.
finish
:
if
self
.
finish
:
assets
=
app_service
.
get_search_user_granted_assets
(
self
.
client
.
user
,
q
)
assets
=
self
.
search_assets_from_local
(
q
)
return
assets
else
:
assets
=
self
.
assets_list
assets
=
self
.
search_assets_from_core
(
q
)
return
assets
def
search_assets_from_core
(
self
,
q
):
assets
=
app_service
.
get_search_user_granted_assets
(
self
.
client
.
user
,
q
)
assets
=
self
.
filter_system_users
(
assets
)
return
assets
def
search_assets_from_local
(
self
,
q
):
result
=
[]
result
=
[]
# 所有的
# 所有的
if
q
in
(
''
,
None
):
if
q
in
(
''
,
None
):
result
=
assets
result
=
self
.
assets
# 全匹配到则直接返回全匹配的
# 全匹配到则直接返回全匹配的
if
len
(
result
)
==
0
:
if
len
(
result
)
==
0
:
_result
=
[
asset
for
asset
in
assets
_result
=
[
asset
for
asset
in
self
.
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
assets
result
=
[
asset
for
asset
in
self
.
assets
if
is_obj_attr_has
(
asset
,
q
)]
if
is_obj_attr_has
(
asset
,
q
)]
return
result
return
result
def
display_assets
(
self
):
#
"""
# Display assets
Display user all assets
#
:return:
"""
self
.
display_result_paging
(
self
.
assets_list
)
def
display_nodes
(
self
):
def
display_assets
(
self
,
assets
=
None
):
if
self
.
nodes
is
None
:
if
assets
is
None
:
self
.
get_user_nodes
()
while
not
self
.
assets
and
not
self
.
finish
:
time
.
sleep
(
0.2
)
assets
=
self
.
assets
self
.
display_assets_paging
(
assets
)
def
display_assets_paging
(
self
,
assets
):
if
len
(
self
.
node
s
)
==
0
:
if
len
(
asset
s
)
==
0
:
self
.
client
.
send
(
w
arning
(
_
(
"No"
)
))
self
.
client
.
send
(
w
r
(
_
(
"No Assets"
),
before
=
0
))
return
return
id_length
=
max
(
len
(
str
(
len
(
self
.
nodes
))),
5
)
self
.
total_count
=
self
.
total_assets
if
assets
is
self
.
assets
else
len
(
assets
)
name_length
=
item_max_length
(
self
.
nodes
,
15
,
key
=
lambda
x
:
x
.
name
)
amount_length
=
item_max_length
(
self
.
nodes
,
10
,
key
=
lambda
x
:
x
.
assets_amount
)
size_list
=
[
id_length
,
name_length
,
amount_length
]
fake_data
=
[
'ID'
,
_
(
"Name"
),
_
(
"Assets"
)]
self
.
client
.
send
(
wr
(
title
(
format_with_zh
(
size_list
,
*
fake_data
))))
action
=
None
for
index
,
node
in
enumerate
(
self
.
nodes
,
1
):
gen
=
self
.
_page_generator
(
assets
)
data
=
[
index
,
node
.
name
,
node
.
assets_amount
]
while
True
:
self
.
client
.
send
(
wr
(
format_with_zh
(
size_list
,
*
data
)))
try
:
self
.
client
.
send
(
wr
(
_
(
"Total: {}"
)
.
format
(
len
(
self
.
nodes
)),
before
=
1
))
page
,
_assets
=
gen
.
send
(
action
)
except
StopIteration
as
e
:
if
None
not
in
e
.
value
:
page
,
_assets
=
e
.
value
self
.
display_a_page_assets
(
page
,
_assets
)
break
else
:
self
.
display_a_page_assets
(
page
,
_assets
)
self
.
display_page_bottom_prompt
()
action
=
self
.
get_user_action
()
def
display_nodes_tree
(
self
):
def
_page_generator
(
self
,
assets
):
if
self
.
nodes
is
None
:
start
,
page
=
0
,
1
self
.
get_user_nodes
()
while
True
:
_assets
=
assets
[
start
:
start
+
self
.
page_size
]
# 等待加载
if
(
assets
is
self
.
assets
)
and
(
not
self
.
finish
)
and
(
not
self
.
need_paging
):
time
.
sleep
(
1
)
continue
# 最后一页
elif
_assets
and
(
page
==
self
.
total_pages
)
and
(
assets
is
not
self
.
assets
or
(
assets
is
self
.
assets
and
self
.
finish
)):
return
page
,
_assets
# 执行动作
else
:
action
=
yield
page
,
_assets
if
not
self
.
nodes
:
# 退出
self
.
client
.
send
(
wr
(
_
(
'No Nodes'
),
before
=
0
))
if
action
==
BACK
:
return
return
None
,
None
# 不分页, 不对页码和下标做更改
elif
not
self
.
need_paging
:
continue
# 上一页
elif
action
==
PAGE_UP
:
if
page
<=
1
:
page
=
1
start
=
0
else
:
page
-=
1
start
-=
self
.
page_size
# 下一页
else
:
page
+=
1
start
+=
len
(
_assets
)
self
.
nodes_tree
.
show
(
key
=
lambda
node
:
node
.
identifier
)
def
display_a_page_assets
(
self
,
page
,
assets
):
self
.
client
.
send
(
wr
(
title
(
_
(
"Node: [ ID.Name(Asset amount) ]"
)),
before
=
0
)
)
self
.
client
.
send
(
char
.
CLEAR_CHAR
)
self
.
client
.
send
(
wr
(
self
.
nodes_tree
.
_reader
.
replace
(
'
\n
'
,
'
\r\n
'
),
before
=
0
))
self
.
page
=
page
prompt
=
_
(
"Tips: Enter g+NodeID to display the host under the node, such as g1"
)
self
.
results
=
assets
self
.
client
.
send
(
wr
(
title
(
prompt
),
before
=
1
)
)
self
.
display_results
(
)
def
display_
node_assets
(
self
,
_id
):
def
display_
page_bottom_prompt
(
self
):
if
self
.
nodes
is
None
:
self
.
client
.
send
(
wr
(
_
(
'Tips: Enter the asset ID and log directly into the asset.'
),
before
=
1
))
self
.
get_user_nodes
(
)
prompt_page_up
=
_
(
"Page up: P/p"
)
if
_id
>
len
(
self
.
nodes
)
or
_id
<=
0
:
prompt_page_down
=
_
(
"Page down: Enter|N/n"
)
msg
=
wr
(
warning
(
_
(
"There is no matched node, please re-enter"
))
)
prompt_back
=
_
(
"BACK: b/q"
)
self
.
client
.
send
(
msg
)
prompts
=
[
prompt_page_up
,
prompt_page_down
,
prompt_back
]
self
.
display_nodes_tree
(
)
prompt
=
'
\t
'
.
join
(
prompts
)
return
self
.
client
.
send
(
wr
(
prompt
,
before
=
1
))
assets
=
self
.
nodes
[
_id
-
1
]
.
assets_granted
def
get_user_action
(
self
):
self
.
display_result_paging
(
assets
)
opt
=
net_input
(
self
.
client
,
prompt
=
':'
,
only_one_char
=
True
)
print
(
opt
)
if
opt
in
(
'p'
,
'P'
):
return
PAGE_UP
elif
opt
in
(
'b'
,
'q'
):
return
BACK
elif
opt
.
isdigit
()
and
self
.
results
and
0
<
int
(
opt
)
<=
len
(
self
.
results
):
self
.
proxy
(
self
.
results
[
int
(
opt
)
-
1
])
return
BACK
else
:
return
PAGE_DOWN
def
display_
search_result
(
self
):
def
display_
results
(
self
):
sort_by
=
config
[
"ASSET_LIST_SORT_BY"
]
sort_by
=
config
[
"ASSET_LIST_SORT_BY"
]
self
.
search_result
=
sort_assets
(
self
.
search_result
,
sort_by
)
self
.
results
=
sort_assets
(
self
.
results
,
sort_by
)
fake_data
=
[
_
(
"ID"
),
_
(
"Hostname"
),
_
(
"IP"
),
_
(
"LoginAs"
)]
fake_data
=
[
_
(
"ID"
),
_
(
"Hostname"
),
_
(
"IP"
),
_
(
"LoginAs"
)]
id_length
=
max
(
len
(
str
(
len
(
self
.
search_result
))),
4
)
id_length
=
max
(
len
(
str
(
len
(
self
.
results
))),
4
)
hostname_length
=
item_max_length
(
self
.
search_result
,
15
,
hostname_length
=
item_max_length
(
self
.
results
,
15
,
key
=
lambda
x
:
x
.
hostname
)
key
=
lambda
x
:
x
.
hostname
)
sysuser_length
=
item_max_length
(
self
.
search_result
,
sysuser_length
=
item_max_length
(
self
.
results
,
key
=
lambda
x
:
x
.
system_users_name_list
)
key
=
lambda
x
:
x
.
system_users_name_list
)
size_list
=
[
id_length
,
hostname_length
,
16
,
sysuser_length
]
size_list
=
[
id_length
,
hostname_length
,
16
,
sysuser_length
]
header_without_comment
=
format_with_zh
(
size_list
,
*
fake_data
)
header_without_comment
=
format_with_zh
(
size_list
,
*
fake_data
)
...
@@ -224,43 +312,110 @@ class InteractiveServer:
...
@@ -224,43 +312,110 @@ class InteractiveServer:
size_list
.
append
(
comment_length
)
size_list
.
append
(
comment_length
)
fake_data
.
append
(
_
(
"Comment"
))
fake_data
.
append
(
_
(
"Comment"
))
self
.
client
.
send
(
wr
(
title
(
format_with_zh
(
size_list
,
*
fake_data
))))
self
.
client
.
send
(
wr
(
title
(
format_with_zh
(
size_list
,
*
fake_data
))))
for
index
,
asset
in
enumerate
(
self
.
search_result
,
1
):
for
index
,
asset
in
enumerate
(
self
.
results
,
1
):
data
=
[
data
=
[
index
,
asset
.
hostname
,
asset
.
ip
,
index
,
asset
.
hostname
,
asset
.
ip
,
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
)))
total_page
=
math
.
ceil
(
self
.
total_count
/
self
.
page_size
)
self
.
client
.
send
(
wr
(
title
(
_
(
"Page: {}, Count: {}, Total Page: {}, Total Count: {}"
)
.
format
(
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
)
self
.
page
,
len
(
self
.
results
),
self
.
total_pages
,
self
.
total_count
)),
before
=
1
)
)
)
def
search_and_display
(
self
,
q
):
#
assets
=
self
.
search_assets
(
q
)
# Get assets
self
.
display_result_paging
(
assets
)
#
def
get_user_nodes
(
self
):
def
get_user_assets_async
(
self
):
self
.
nodes
=
app_service
.
get_user_asset_groups
(
self
.
client
.
user
)
if
self
.
need_paging
:
self
.
sort_nodes
()
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets_paging
)
self
.
construct_nodes_tree
()
else
:
thread
=
threading
.
Thread
(
target
=
self
.
get_user_assets_direct
)
thread
.
start
()
def
get_user_assets_direct
(
self
):
assets
=
app_service
.
get_user_assets
(
self
.
client
.
user
)
assets
=
self
.
filter_system_users
(
assets
)
self
.
assets
=
assets
self
.
total_assets
=
len
(
assets
)
self
.
finish
=
True
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
)
if
not
assets
:
logger
.
info
(
'Get user assets paging async finished.'
)
self
.
finish
=
True
break
def
sort_nodes
(
self
):
logger
.
info
(
'Get user assets paging async: {}'
.
format
(
len
(
assets
)))
self
.
nodes
=
sorted
(
self
.
nodes
,
key
=
lambda
node
:
node
.
key
)
assets
=
self
.
filter_system_users
(
assets
)
self
.
total_assets
=
total
self
.
assets
.
extend
(
assets
)
self
.
offset
+=
self
.
limit
#
# Nodes
#
def
get_user_nodes_async
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
get_user_nodes
)
thread
.
start
()
def
construct_nodes_tree
(
self
):
def
get_user_nodes
(
self
):
self
.
nodes_tree
=
Tree
()
nodes
=
app_service
.
get_user_asset_groups
(
self
.
client
.
user
)
nodes
=
sorted
(
nodes
,
key
=
lambda
node
:
node
.
key
)
self
.
nodes
=
self
.
filter_system_users_of_assets_under_nodes
(
nodes
)
self
.
_construct_node_tree
()
def
filter_system_users_of_assets_under_nodes
(
self
,
nodes
):
for
node
in
nodes
:
node
.
assets_granted
=
self
.
filter_system_users
(
node
.
assets_granted
)
return
nodes
def
_construct_node_tree
(
self
):
self
.
node_tree
=
Tree
()
root
=
'ROOT_ALL_ORG_NODE'
root
=
'ROOT_ALL_ORG_NODE'
self
.
node
s
_tree
.
create_node
(
tag
=
''
,
identifier
=
root
,
parent
=
None
)
self
.
node_tree
.
create_node
(
tag
=
''
,
identifier
=
root
,
parent
=
None
)
for
index
,
node
in
enumerate
(
self
.
nodes
):
for
index
,
node
in
enumerate
(
self
.
nodes
):
tag
=
"{}.{}({})"
.
format
(
index
+
1
,
node
.
name
,
node
.
assets_amount
)
tag
=
"{}.{}({})"
.
format
(
index
+
1
,
node
.
name
,
node
.
assets_amount
)
key
=
node
.
key
key
=
node
.
key
parent_key
=
key
[:
node
.
key
.
rfind
(
':'
)]
or
root
parent_key
=
key
[:
node
.
key
.
rfind
(
':'
)]
or
root
self
.
node
s
_tree
.
create_node
(
tag
=
tag
,
identifier
=
key
,
data
=
node
,
parent
=
parent_key
)
self
.
node_tree
.
create_node
(
tag
=
tag
,
identifier
=
key
,
data
=
node
,
parent
=
parent_key
)
def
get_user_nodes_async
(
self
):
def
display_nodes_as_tree
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
get_user_nodes
)
if
self
.
nodes
is
None
:
thread
.
start
()
self
.
get_user_nodes
()
if
not
self
.
nodes
:
self
.
client
.
send
(
wr
(
_
(
'No Nodes'
),
before
=
0
))
return
self
.
node_tree
.
show
(
key
=
lambda
node
:
node
.
identifier
)
self
.
client
.
send
(
wr
(
title
(
_
(
"Node: [ ID.Name(Asset amount) ]"
)),
before
=
0
))
self
.
client
.
send
(
wr
(
self
.
node_tree
.
_reader
.
replace
(
'
\n
'
,
'
\r\n
'
),
before
=
0
))
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_as_tree
()
return
assets
=
self
.
nodes
[
_id
-
1
]
.
assets_granted
self
.
display_assets
(
assets
)
#
# System users
#
@staticmethod
@staticmethod
def
filter_system_users
(
assets
):
def
filter_system_users
(
assets
):
...
@@ -273,26 +428,6 @@ class InteractiveServer:
...
@@ -273,26 +428,6 @@ class InteractiveServer:
asset
.
system_users_granted
=
system_users_cleaned
asset
.
system_users_granted
=
system_users_cleaned
return
assets
return
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
):
def
choose_system_user
(
self
,
system_users
):
if
len
(
system_users
)
==
1
:
if
len
(
system_users
)
==
1
:
return
system_users
[
0
]
return
system_users
[
0
]
...
@@ -316,124 +451,9 @@ class InteractiveServer:
...
@@ -316,124 +451,9 @@ class InteractiveServer:
for
index
,
system_user
in
enumerate
(
system_users
):
for
index
,
system_user
in
enumerate
(
system_users
):
self
.
client
.
send
(
wr
(
"{} {}"
.
format
(
index
,
system_user
.
name
)))
self
.
client
.
send
(
wr
(
"{} {}"
.
format
(
index
,
system_user
.
name
)))
def
search_and_proxy
(
self
,
opt
):
#
assets
=
self
.
search_assets
(
opt
)
# Proxy
if
assets
and
len
(
assets
)
==
1
:
#
asset
=
assets
[
0
]
self
.
search_result
=
None
if
asset
.
protocol
==
"rdp"
or
asset
.
platform
.
lower
()
.
startswith
(
"windows"
):
self
.
client
.
send
(
warning
(
_
(
"Terminal does not support login rdp, "
"please use web terminal to access"
))
)
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
=
0
))
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
)
...
@@ -443,6 +463,10 @@ class InteractiveServer:
...
@@ -443,6 +463,10 @@ class InteractiveServer:
forwarder
=
ProxyServer
(
self
.
client
,
asset
,
system_user
)
forwarder
=
ProxyServer
(
self
.
client
,
asset
,
system_user
)
forwarder
.
proxy
()
forwarder
.
proxy
()
#
# Entrance
#
def
interact
(
self
):
def
interact
(
self
):
self
.
display_banner
()
self
.
display_banner
()
while
not
self
.
closed
:
while
not
self
.
closed
:
...
@@ -456,15 +480,16 @@ class InteractiveServer:
...
@@ -456,15 +480,16 @@ class InteractiveServer:
break
break
self
.
close
()
self
.
close
()
def
interact_async
(
self
):
thread
=
threading
.
Thread
(
target
=
self
.
interact
)
thread
.
daemon
=
True
thread
.
start
()
def
close
(
self
):
def
close
(
self
):
logger
.
debug
(
"Interactive server server close: {}"
.
format
(
self
))
logger
.
debug
(
"Interactive server server close: {}"
.
format
(
self
))
self
.
closed
=
True
self
.
closed
=
True
# current_app.remove_client(self.client)
# current_app.remove_client(self.client)
def
interact_async
(
self
):
# 目前没用
thread
=
threading
.
Thread
(
target
=
self
.
interact
)
thread
.
daemon
=
True
thread
.
start
()
# def __del__(self):
# def __del__(self):
# print("GC: Interactive class been gc")
# print("GC: Interactive class been gc")
coco/logger.py
View file @
2983f06a
...
@@ -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'
],
...
...
coco/models.py
View file @
2983f06a
...
@@ -360,6 +360,11 @@ class TelnetServer(BaseServer):
...
@@ -360,6 +360,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
):
"""
"""
...
...
coco/sshd.py
View file @
2983f06a
...
@@ -63,6 +63,7 @@ class SSHServer:
...
@@ -63,6 +63,7 @@ class SSHServer:
return
connection
return
connection
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
()
...
@@ -77,7 +78,7 @@ class SSHServer:
...
@@ -77,7 +78,7 @@ class SSHServer:
server
=
SSHInterface
(
connection
)
server
=
SSHInterface
(
connection
)
try
:
try
:
transport
.
start_server
(
server
=
server
)
transport
.
start_server
(
server
=
server
)
except
paramiko
.
SSHException
:
except
(
paramiko
.
SSHException
,
socket
.
timeout
)
:
logger
.
warning
(
"SSH negotiation failed"
)
logger
.
warning
(
"SSH negotiation failed"
)
return
return
except
EOFError
as
e
:
except
EOFError
as
e
:
...
...
coco/utils.py
View file @
2983f06a
...
@@ -294,7 +294,7 @@ def get_logger(file_name):
...
@@ -294,7 +294,7 @@ def get_logger(file_name):
return
logging
.
getLogger
(
'coco.'
+
file_name
)
return
logging
.
getLogger
(
'coco.'
+
file_name
)
def
net_input
(
client
,
prompt
=
'Opt> '
,
sensitive
=
False
,
before
=
0
,
after
=
0
):
def
net_input
(
client
,
prompt
=
'Opt> '
,
sensitive
=
False
,
before
=
0
,
after
=
0
,
only_one_char
=
False
):
"""实现了一个ssh input, 提示用户输入, 获取并返回
"""实现了一个ssh input, 提示用户输入, 获取并返回
:return user input string
:return user input string
...
@@ -303,6 +303,10 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0):
...
@@ -303,6 +303,10 @@ def net_input(client, prompt='Opt> ', sensitive=False, before=0, after=0):
parser
=
TtyIOParser
()
parser
=
TtyIOParser
()
client
.
send
(
wrap_with_line_feed
(
prompt
,
before
=
before
,
after
=
after
))
client
.
send
(
wrap_with_line_feed
(
prompt
,
before
=
before
,
after
=
after
))
if
only_one_char
:
data
=
client
.
recv
(
1
)
return
data
.
decode
()
while
True
:
while
True
:
data
=
client
.
recv
(
10
)
data
=
client
.
recv
(
10
)
if
len
(
data
)
==
0
:
if
len
(
data
)
==
0
:
...
...
locale/en/LC_MESSAGES/coco.mo
View file @
2983f06a
No preview for this file type
locale/en/LC_MESSAGES/coco.po
View file @
2983f06a
...
@@ -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-1
0-31 11:49
+0800\n"
"POT-Creation-Date: 2018-1
2-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:1
3
5
#: coco/app.py:1
4
5
msgid "Connect idle more than {} minutes, disconnect"
msgid "Connect idle more than {} minutes, disconnect"
msgstr ""
msgstr ""
#: coco/interactive.py:8
0
#: coco/interactive.py:8
4
#, python-brace-format
#, python-brace-format
msgid ""
msgid ""
"\n"
"\n"
...
@@ -28,136 +28,127 @@ msgid ""
...
@@ -28,136 +28,127 @@ msgid ""
"{end}{R}{R}"
"{end}{R}{R}"
msgstr ""
msgstr ""
#: coco/interactive.py:8
2
#: coco/interactive.py:8
6
#, 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:8
3
#: coco/interactive.py:8
7
#, 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:8
4
#: coco/interactive.py:8
8
#, 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:8
5
#: coco/interactive.py:8
9
#, 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}8) Enter {green}r{end} to refresh your assets and nodes.{R}"
msgid "{T}8) Enter {green}r{end} to refresh your assets and nodes.{R}"
msgstr ""
msgstr ""
#: coco/interactive.py:9
0
#: coco/interactive.py:9
4
#, 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:159
#: coco/interactive.py:155
msgid "No"
msgid "Terminal does not support login rdp, please use web terminal to access"
msgstr ""
#: coco/interactive.py:166
msgid "Name"
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 "T
otal: {}
"
msgid "T
ips: 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:40
1
#: coco/interactive.py:40
0
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:4
04
#: coco/interactive.py:4
38
msgid "
BACK: B/b
"
msgid "
Select a login::
"
msgstr ""
msgstr ""
#: coco/interactive.py:4
25
#: coco/interactive.py:4
61
msgid "No system user"
msgid "No system user"
msgstr ""
msgstr ""
...
...
locale/zh_CN/LC_MESSAGES/coco.mo
View file @
2983f06a
No preview for this file type
locale/zh_CN/LC_MESSAGES/coco.po
View file @
2983f06a
...
@@ -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-1
0-31 11:49
+0800\n"
"POT-Creation-Date: 2018-1
2-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:1
3
5
#: coco/app.py:1
4
5
msgid "Connect idle more than {} minutes, disconnect"
msgid "Connect idle more than {} minutes, disconnect"
msgstr "空闲时间超过 {} 分钟,断开连接"
msgstr "空闲时间超过 {} 分钟,断开连接"
#: coco/interactive.py:8
0
#: coco/interactive.py:8
4
#, 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:8
2
#: coco/interactive.py:8
6
#, 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:8
3
#: coco/interactive.py:8
7
#, 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,127 +48,120 @@ msgstr ""
...
@@ -48,127 +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:8
4
#: coco/interactive.py:8
8
#, 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:8
5
#: coco/interactive.py:8
9
#, 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
#, python-brace-format
msgid "{T}8) Enter {green}r{end} to refresh your assets and nodes.{R}"
msgid "{T}8) Enter {green}r{end} to refresh your assets and nodes.{R}"
msgstr "{T}0) 输入 {green}r{end} 刷新最新的机器和节点信息.{R}"
msgstr "{T}0) 输入 {green}r{end} 刷新最新的机器和节点信息.{R}"
#: coco/interactive.py:9
0
#: coco/interactive.py:9
4
#, 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 "T
otal: {}
"
msgid "T
ips: 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:40
2
#: coco/interactive.py:40
0
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:40
3
#: coco/interactive.py:40
8
msgid "
Page down: Enter|N/n
"
msgid "
There is no matched node, please re-enter
"
msgstr "
下一页: Enter|N/n
"
msgstr "
没有匹配分组,请重新输入
"
#: coco/interactive.py:4
04
#: coco/interactive.py:4
38
msgid "
BACK: B/b
"
msgid "
Select a login::
"
msgstr "
返回: B/b
"
msgstr "
选择一个登录:
"
#: coco/interactive.py:4
25
#: coco/interactive.py:4
61
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"
...
@@ -182,5 +175,14 @@ msgstr "开始连接到 {}@{} {:.1f}"
...
@@ -182,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 "总共: {} 匹配: {}"
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