Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
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
jumpserver
Commits
0c9e24dc
Commit
0c9e24dc
authored
Dec 10, 2017
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Feture] 添加ops 页面
parent
18fd04d6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
41 changed files
with
1043 additions
and
449 deletions
+1043
-449
api.py
apps/assets/api.py
+5
-4
models.py
apps/assets/models.py
+0
-2
__init__.py
apps/assets/models/__init__.py
+0
-2
asset.py
apps/assets/models/asset.py
+5
-0
group.py
apps/assets/models/group.py
+0
-1
user.py
apps/assets/models/user.py
+0
-19
serializers.py
apps/assets/serializers.py
+23
-5
tasks.py
apps/assets/tasks.py
+213
-21
admin_user_assets.html
apps/assets/templates/assets/admin_user_assets.html
+0
-0
admin_user_detail.html
apps/assets/templates/assets/admin_user_detail.html
+0
-0
admin_user_list.html
apps/assets/templates/assets/admin_user_list.html
+3
-6
system_user_asset.html
apps/assets/templates/assets/system_user_asset.html
+2
-4
system_user_list.html
apps/assets/templates/assets/system_user_list.html
+1
-1
views_urls.py
apps/assets/urls/views_urls.py
+1
-0
utils.py
apps/assets/utils.py
+5
-38
admin_user.py
apps/assets/views/admin_user.py
+26
-1
system_user.py
apps/assets/views/system_user.py
+3
-10
celery.py
apps/common/celery.py
+2
-2
utils.py
apps/common/utils.py
+5
-0
settings.py
apps/jumpserver/settings.py
+1
-0
callback.py
apps/ops/ansible/callback.py
+14
-13
inventory.py
apps/ops/ansible/inventory.py
+36
-25
runner.py
apps/ops/ansible/runner.py
+23
-34
test_inventory.py
apps/ops/ansible/test_inventory.py
+2
-2
test_runner.py
apps/ops/ansible/test_runner.py
+5
-2
api.py
apps/ops/api.py
+30
-3
inventory.py
apps/ops/inventory.py
+46
-0
models.py
apps/ops/models.py
+107
-34
serializers.py
apps/ops/serializers.py
+33
-2
tasks.py
apps/ops/tasks.py
+6
-15
task_adhoc.html
apps/ops/templates/ops/task_adhoc.html
+117
-0
task_detail.html
apps/ops/templates/ops/task_detail.html
+29
-50
task_history.html
apps/ops/templates/ops/task_history.html
+123
-0
task_list.html
apps/ops/templates/ops/task_list.html
+13
-15
test_utils.py
apps/ops/test_utils.py
+4
-5
api_urls.py
apps/ops/urls/api_urls.py
+2
-0
view_urls.py
apps/ops/urls/view_urls.py
+3
-0
utils.py
apps/ops/utils.py
+112
-80
views.py
apps/ops/views.py
+41
-16
tasks.py
apps/perms/tasks.py
+1
-36
_nav.html
apps/templates/_nav.html
+1
-1
No files found.
apps/assets/api.py
View file @
0c9e24dc
...
@@ -26,12 +26,13 @@ from .hands import IsSuperUser, IsAppUser, IsValidUser, \
...
@@ -26,12 +26,13 @@ from .hands import IsSuperUser, IsAppUser, IsValidUser, \
get_user_granted_assets
,
push_users
get_user_granted_assets
,
push_users
from
.models
import
AssetGroup
,
Asset
,
Cluster
,
SystemUser
,
AdminUser
from
.models
import
AssetGroup
,
Asset
,
Cluster
,
SystemUser
,
AdminUser
from
.
import
serializers
from
.
import
serializers
from
.tasks
import
update_assets_hardware_info
from
.tasks
import
update_assets_hardware_info
,
test_admin_user_connectability_manual
from
.utils
import
test_admin_user_connective_manual
class
AssetViewSet
(
IDInFilterMixin
,
BulkModelViewSet
):
class
AssetViewSet
(
IDInFilterMixin
,
BulkModelViewSet
):
"""API endpoint that allows Asset to be viewed or edited."""
"""
API endpoint that allows Asset to be viewed or edited.
"""
queryset
=
Asset
.
objects
.
all
()
queryset
=
Asset
.
objects
.
all
()
serializer_class
=
serializers
.
AssetSerializer
serializer_class
=
serializers
.
AssetSerializer
permission_classes
=
(
IsValidUser
,)
permission_classes
=
(
IsValidUser
,)
...
@@ -195,7 +196,7 @@ class AssetAdminUserTestView(AssetRefreshHardwareView):
...
@@ -195,7 +196,7 @@ class AssetAdminUserTestView(AssetRefreshHardwareView):
def
retrieve
(
self
,
request
,
*
args
,
**
kwargs
):
def
retrieve
(
self
,
request
,
*
args
,
**
kwargs
):
asset_id
=
kwargs
.
get
(
'pk'
)
asset_id
=
kwargs
.
get
(
'pk'
)
asset
=
get_object_or_404
(
Asset
,
pk
=
asset_id
)
asset
=
get_object_or_404
(
Asset
,
pk
=
asset_id
)
result
=
test_admin_user_connect
ive_manual
([
asset
]
)
result
=
test_admin_user_connect
ability_manual
(
asset
)
if
result
:
if
result
:
return
Response
(
'1'
)
return
Response
(
'1'
)
else
:
else
:
...
...
apps/assets/models.py
deleted
100644 → 0
View file @
18fd04d6
# -*- coding: utf-8 -*-
#
apps/assets/models/__init__.py
View file @
0c9e24dc
#!/usr/bin/env python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
#
print
(
"Import assets model"
)
from
.user
import
AdminUser
,
SystemUser
from
.user
import
AdminUser
,
SystemUser
from
.cluster
import
*
from
.cluster
import
*
from
.group
import
*
from
.group
import
*
...
...
apps/assets/models/asset.py
View file @
0c9e24dc
...
@@ -7,6 +7,7 @@ import uuid
...
@@ -7,6 +7,7 @@ import uuid
from
django.db
import
models
from
django.db
import
models
import
logging
import
logging
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.core.cache
import
cache
from
.cluster
import
Cluster
from
.cluster
import
Cluster
from
.group
import
AssetGroup
from
.group
import
AssetGroup
...
@@ -21,6 +22,7 @@ def get_default_cluster():
...
@@ -21,6 +22,7 @@ def get_default_cluster():
class
Asset
(
models
.
Model
):
class
Asset
(
models
.
Model
):
# Todo: Move them to settings
STATUS_CHOICES
=
(
STATUS_CHOICES
=
(
(
'In use'
,
_
(
'In use'
)),
(
'In use'
,
_
(
'In use'
)),
(
'Out of use'
,
_
(
'Out of use'
)),
(
'Out of use'
,
_
(
'Out of use'
)),
...
@@ -103,6 +105,9 @@ class Asset(models.Model):
...
@@ -103,6 +105,9 @@ class Asset(models.Model):
'groups'
:
[
group
.
name
for
group
in
self
.
groups
.
all
()],
'groups'
:
[
group
.
name
for
group
in
self
.
groups
.
all
()],
}
}
def
is_connective
(
self
):
return
cache
.
get
(
self
.
hostname
)
def
_to_secret_json
(
self
):
def
_to_secret_json
(
self
):
"""
"""
Ansible use it create inventory
Ansible use it create inventory
...
...
apps/assets/models/group.py
View file @
0c9e24dc
...
@@ -19,7 +19,6 @@ logger = logging.getLogger(__name__)
...
@@ -19,7 +19,6 @@ logger = logging.getLogger(__name__)
class
AssetGroup
(
models
.
Model
):
class
AssetGroup
(
models
.
Model
):
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
name
=
models
.
CharField
(
max_length
=
64
,
unique
=
True
,
verbose_name
=
_
(
'Name'
))
name
=
models
.
CharField
(
max_length
=
64
,
unique
=
True
,
verbose_name
=
_
(
'Name'
))
system_users
=
models
.
ManyToManyField
(
SystemUser
,
related_name
=
'asset_groups'
,
blank
=
True
)
created_by
=
models
.
CharField
(
max_length
=
32
,
blank
=
True
,
verbose_name
=
_
(
'Created by'
))
created_by
=
models
.
CharField
(
max_length
=
32
,
blank
=
True
,
verbose_name
=
_
(
'Created by'
))
date_created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
verbose_name
=
_
(
'Date created'
))
date_created
=
models
.
DateTimeField
(
auto_now_add
=
True
,
null
=
True
,
verbose_name
=
_
(
'Date created'
))
comment
=
models
.
TextField
(
blank
=
True
,
verbose_name
=
_
(
'Comment'
))
comment
=
models
.
TextField
(
blank
=
True
,
verbose_name
=
_
(
'Comment'
))
...
...
apps/assets/models/user.py
View file @
0c9e24dc
...
@@ -201,21 +201,6 @@ class SystemUser(models.Model):
...
@@ -201,21 +201,6 @@ class SystemUser(models.Model):
def
public_key
(
self
,
public_key_raw
):
def
public_key
(
self
,
public_key_raw
):
self
.
_public_key
=
signer
.
sign
(
public_key_raw
)
self
.
_public_key
=
signer
.
sign
(
public_key_raw
)
def
get_assets_inherit_from_asset_groups
(
self
):
assets
=
set
()
asset_groups
=
self
.
asset_groups
.
all
()
for
asset_group
in
asset_groups
:
for
asset
in
asset_group
.
assets
.
all
():
setattr
(
asset
,
'is_inherit_from_asset_groups'
,
True
)
setattr
(
asset
,
'inherit_from_asset_groups'
,
getattr
(
asset
,
'inherit_from_asset_groups'
,
set
())
.
add
(
asset_group
))
assets
.
add
(
asset
)
return
assets
def
get_assets
(
self
):
assets
=
set
(
self
.
assets
.
all
())
|
self
.
get_assets_inherit_from_asset_groups
()
return
list
(
assets
)
def
_to_secret_json
(
self
):
def
_to_secret_json
(
self
):
"""Push system user use it"""
"""Push system user use it"""
return
{
return
{
...
@@ -232,10 +217,6 @@ class SystemUser(models.Model):
...
@@ -232,10 +217,6 @@ class SystemUser(models.Model):
def
assets_amount
(
self
):
def
assets_amount
(
self
):
return
self
.
assets
.
count
()
return
self
.
assets
.
count
()
@property
def
asset_group_amount
(
self
):
return
self
.
asset_groups
.
count
()
def
to_json
(
self
):
def
to_json
(
self
):
return
{
return
{
'id'
:
self
.
id
,
'id'
:
self
.
id
,
...
...
apps/assets/serializers.py
View file @
0c9e24dc
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
rest_framework
import
viewsets
,
serializers
,
generics
from
rest_framework
import
viewsets
,
serializers
,
generics
from
.models
import
AssetGroup
,
Asset
,
Cluster
,
AdminUser
,
SystemUser
from
common.mixins
import
IDInFilterMixin
from
rest_framework_bulk
import
BulkListSerializer
,
BulkSerializerMixin
from
rest_framework_bulk
import
BulkListSerializer
,
BulkSerializerMixin
from
.models
import
AssetGroup
,
Asset
,
Cluster
,
AdminUser
,
SystemUser
from
.tasks
import
SYSTEM_USER_CONN_CACHE_KEY_PREFIX
,
ADMIN_USER_CONN_CACHE_KEY_PREFIX
class
AssetGroupSerializer
(
BulkSerializerMixin
,
serializers
.
ModelSerializer
):
class
AssetGroupSerializer
(
BulkSerializerMixin
,
serializers
.
ModelSerializer
):
assets_amount
=
serializers
.
SerializerMethodField
()
assets_amount
=
serializers
.
SerializerMethodField
()
...
@@ -64,11 +64,20 @@ class ClusterUpdateAssetsSerializer(serializers.ModelSerializer):
...
@@ -64,11 +64,20 @@ class ClusterUpdateAssetsSerializer(serializers.ModelSerializer):
class
AdminUserSerializer
(
serializers
.
ModelSerializer
):
class
AdminUserSerializer
(
serializers
.
ModelSerializer
):
assets
=
serializers
.
PrimaryKeyRelatedField
(
many
=
True
,
queryset
=
Asset
.
objects
.
all
())
assets
=
serializers
.
PrimaryKeyRelatedField
(
many
=
True
,
queryset
=
Asset
.
objects
.
all
())
unreachable_amount
=
serializers
.
SerializerMethodField
()
class
Meta
:
class
Meta
:
model
=
AdminUser
model
=
AdminUser
fields
=
'__all__'
fields
=
'__all__'
@staticmethod
def
get_unreachable_amount
(
obj
):
data
=
cache
.
get
(
ADMIN_USER_CONN_CACHE_KEY_PREFIX
+
obj
.
name
)
if
data
:
return
len
(
data
.
get
(
'dark'
))
else
:
return
'Unknown'
def
get_field_names
(
self
,
declared_fields
,
info
):
def
get_field_names
(
self
,
declared_fields
,
info
):
fields
=
super
(
AdminUserSerializer
,
self
)
.
get_field_names
(
declared_fields
,
info
)
fields
=
super
(
AdminUserSerializer
,
self
)
.
get_field_names
(
declared_fields
,
info
)
fields
.
append
(
'assets_amount'
)
fields
.
append
(
'assets_amount'
)
...
@@ -76,10 +85,20 @@ class AdminUserSerializer(serializers.ModelSerializer):
...
@@ -76,10 +85,20 @@ class AdminUserSerializer(serializers.ModelSerializer):
class
SystemUserSerializer
(
serializers
.
ModelSerializer
):
class
SystemUserSerializer
(
serializers
.
ModelSerializer
):
unreachable_amount
=
serializers
.
SerializerMethodField
()
class
Meta
:
class
Meta
:
model
=
SystemUser
model
=
SystemUser
exclude
=
(
'_password'
,
'_private_key'
,
'_public_key'
)
exclude
=
(
'_password'
,
'_private_key'
,
'_public_key'
)
@staticmethod
def
get_unreachable_amount
(
obj
):
data
=
cache
.
get
(
SYSTEM_USER_CONN_CACHE_KEY_PREFIX
+
obj
.
name
)
if
data
:
return
len
(
data
.
get
(
'dark'
))
else
:
return
"Unknown"
def
get_field_names
(
self
,
declared_fields
,
info
):
def
get_field_names
(
self
,
declared_fields
,
info
):
fields
=
super
(
SystemUserSerializer
,
self
)
.
get_field_names
(
declared_fields
,
info
)
fields
=
super
(
SystemUserSerializer
,
self
)
.
get_field_names
(
declared_fields
,
info
)
fields
.
extend
([
'assets_amount'
])
fields
.
extend
([
'assets_amount'
])
...
@@ -167,8 +186,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
...
@@ -167,8 +186,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
@staticmethod
@staticmethod
def
get_system_users_join
(
obj
):
def
get_system_users_join
(
obj
):
return
', '
.
join
([
system_user
.
username
return
', '
.
join
([
system_user
.
username
for
system_user
in
obj
.
system_users_granted
])
for
system_user
in
obj
.
system_users_granted
])
class
MyAssetGrantedSerializer
(
AssetGrantedSerializer
):
class
MyAssetGrantedSerializer
(
AssetGrantedSerializer
):
...
...
apps/assets/tasks.py
View file @
0c9e24dc
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
from
celery
import
shared_task
import
json
import
json
from
celery
import
shared_task
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
ops.tasks
import
run_AdHoc
from
assets.models
import
SystemUser
,
AdminUser
from
common.utils
import
get_object_or_none
,
capacity_convert
,
sum_capacity
from
common.utils
import
get_object_or_none
,
capacity_convert
,
sum_capacity
,
encrypt_password
,
get_logger
from
.models
import
Asset
from
.models
import
Asset
FORKS
=
10
TIMEOUT
=
60
logger
=
get_logger
(
__file__
)
ADMIN_USER_CONN_CACHE_KEY_PREFIX
=
"ADMIN_USER_CONN_"
SYSTEM_USER_CONN_CACHE_KEY_PREFIX
=
'SYSTEM_USER_CONN_'
@shared_task
@shared_task
def
update_assets_hardware_info
(
assets
):
def
update_assets_hardware_info
(
assets
):
task_tuple
=
(
"""
(
'setup'
,
''
),
Using ansible api to update asset hardware info
)
:param assets: asset seq
summary
,
result
=
run_AdHoc
(
task_tuple
,
assets
,
record
=
False
)
:return: result summary ['contacted': {}, 'dark': {}]
for
hostname
,
info
in
result
[
'contacted'
]
.
items
():
"""
from
ops.utils
import
run_adhoc
name
=
"GET_ASSETS_HARDWARE_INFO"
tasks
=
[
{
'name'
:
name
,
'action'
:
{
'module'
:
'setup'
}
}
]
hostname_list
=
[
asset
.
hostname
for
asset
in
assets
]
result
=
run_adhoc
(
hostname_list
,
pattern
=
'all'
,
tasks
=
tasks
,
name
=
name
,
run_as_admin
=
True
)
summary
,
result_raw
=
result
.
results_summary
,
result
.
results_raw
for
hostname
,
info
in
result_raw
[
'ok'
]
.
items
():
if
info
:
if
info
:
info
=
info
[
0
][
'ansible_facts'
]
info
=
info
[
name
][
'ansible_facts'
]
else
:
else
:
continue
continue
asset
=
get_object_or_none
(
Asset
,
hostname
=
hostname
)
asset
=
get_object_or_none
(
Asset
,
hostname
=
hostname
)
...
@@ -58,23 +80,193 @@ def update_assets_hardware_info(assets):
...
@@ -58,23 +80,193 @@ def update_assets_hardware_info(assets):
@shared_task
@shared_task
def
update_assets_hardware_period
():
def
update_assets_hardware_period
():
"""
Update asset hardware period task
:return:
"""
assets
=
Asset
.
objects
.
filter
(
type__in
=
[
'Server'
,
'VM'
])
assets
=
Asset
.
objects
.
filter
(
type__in
=
[
'Server'
,
'VM'
])
update_assets_hardware_info
(
assets
)
update_assets_hardware_info
(
assets
)
@shared_task
@shared_task
def
test_admin_user_connective_period
():
def
test_admin_user_connectability
(
admin_user
):
assets
=
Asset
.
objects
.
filter
(
type__in
=
[
'Server'
,
'VM'
])
"""
task_tuple
=
(
Test asset admin user can connect or not. Using ansible api do that
(
'ping'
,
''
),
:param admin_user:
)
:return:
summary
,
_
=
run_AdHoc
(
task_tuple
,
assets
,
record
=
False
)
"""
for
i
in
summary
[
'success'
]:
from
ops.utils
import
run_adhoc
cache
.
set
(
i
,
'1'
,
2
*
60
*
60
*
60
)
assets
=
admin_user
.
assets
.
all
()
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
for
i
,
msg
in
summary
[
'failed'
]:
hosts
=
[
asset
.
hostname
for
asset
in
assets
]
cache
.
set
(
i
,
'0'
,
60
*
60
*
60
)
tasks
=
[
return
summary
{
"name"
:
"TEST_ADMIN_CONNECTIVE"
,
"action"
:
{
"module"
:
"ping"
,
}
}
]
result
=
run_adhoc
(
hosts
,
tasks
=
tasks
,
pattern
=
"all"
,
run_as_admin
=
True
)
return
result
.
results_summary
@shared_task
def
test_admin_user_connectability_period
():
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
admin_users
=
AdminUser
.
objects
.
all
()
for
admin_user
in
admin_users
:
summary
=
test_admin_user_connectability
(
admin_user
)
cache
.
set
(
ADMIN_USER_CONN_CACHE_KEY_PREFIX
+
admin_user
.
name
,
summary
,
60
*
60
*
60
)
for
i
in
summary
[
'contacted'
]:
cache
.
set
(
ADMIN_USER_CONN_CACHE_KEY_PREFIX
+
i
,
1
,
60
*
60
*
60
)
for
i
in
summary
[
'dark'
]:
cache
.
set
(
ADMIN_USER_CONN_CACHE_KEY_PREFIX
+
i
,
0
,
60
*
60
*
60
)
def
test_admin_user_connectability_manual
(
asset
):
from
ops.utils
import
run_adhoc
# assets = Asset.objects.filter(type__in=['Server', 'VM'])
hosts
=
[
asset
.
hostname
]
tasks
=
[
{
"name"
:
"TEST_ADMIN_CONNECTIVE"
,
"action"
:
{
"module"
:
"ping"
,
}
}
]
result
=
run_adhoc
(
hosts
,
tasks
=
tasks
,
pattern
=
"all"
,
run_as_admin
=
True
)
if
result
.
results_summary
[
'dark'
]:
return
False
else
:
return
True
@shared_task
def
test_system_user_connectability
(
system_user
):
"""
Test system cant connect his assets or not.
:param system_user:
:return:
"""
from
ops.utils
import
run_adhoc
assets
=
system_user
.
assets
.
all
()
hosts
=
[
asset
.
hostname
for
asset
in
assets
]
tasks
=
[
{
"name"
:
"TEST_SYSTEM_USER_CONNECTIVE"
,
"action"
:
{
"module"
:
"ping"
,
}
}
]
result
=
run_adhoc
(
hosts
,
tasks
=
tasks
,
pattern
=
"all"
,
run_as
=
system_user
.
name
)
return
result
.
results_summary
@shared_task
def
test_system_user_connectability_period
():
for
system_user
in
SystemUser
.
objects
.
all
():
summary
=
test_system_user_connectability
(
system_user
)
cache
.
set
(
SYSTEM_USER_CONN_CACHE_KEY_PREFIX
+
system_user
.
name
,
summary
,
60
*
60
*
60
)
def
get_push_system_user_tasks
(
system_user
):
tasks
=
[
{
'name'
:
'Add user'
,
'action'
:
{
'module'
:
'user'
,
'args'
:
'name={} shell={} state=present password={}'
.
format
(
system_user
.
username
,
system_user
.
shell
,
encrypt_password
(
system_user
.
password
),
),
}
},
{
'name'
:
'Set authorized key'
,
'action'
:
{
'module'
:
'authorized_key'
,
'args'
:
"user={} state=present key='{}'"
.
format
(
system_user
.
username
,
system_user
.
public_key
)
}
},
{
'name'
:
'Set sudoers'
,
'action'
:
{
'module'
:
'lineinfile'
,
'args'
:
"dest=/etc/sudoers state=present regexp='^{0} ALL=' "
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
"validate='visudo -cf
%
s'"
.
format
(
system_user
.
username
,
system_user
.
sudo
,
)
}
}
]
return
tasks
PUSH_SYSTEM_USER_PERIOD_TASK_NAME
=
'PUSH SYSTEM USER {} PERIOD...'
PUSH_SYSTEM_USER_TASK_NAME
=
'PUSH SYSTEM USER {} ASSETS'
def
get_push_system_user_task
(
system_user
):
from
ops.utils
import
get_task_by_name
task
=
get_task_by_name
(
PUSH_SYSTEM_USER_PERIOD_TASK_NAME
.
format
(
system_user
.
name
))
return
task
def
push_system_user
(
system_user
,
assets
,
name
):
from
ops.utils
import
get_task_by_name
,
run_adhoc_object
,
\
create_task
,
create_adhoc
if
system_user
.
auto_push
and
assets
:
task
=
get_task_by_name
(
name
)
if
not
task
:
task
=
create_task
(
name
,
created_by
=
"System"
)
task
.
save
()
tasks
=
get_push_system_user_tasks
(
system_user
)
hosts
=
[
asset
.
hostname
for
asset
in
assets
]
options
=
{
'forks'
:
FORKS
,
'timeout'
:
TIMEOUT
}
adhoc
=
task
.
get_latest_adhoc
()
if
not
adhoc
or
adhoc
.
task
!=
tasks
or
adhoc
.
hosts
!=
hosts
:
adhoc
=
create_adhoc
(
task
=
task
,
tasks
=
tasks
,
pattern
=
'all'
,
options
=
options
,
hosts
=
hosts
,
run_as_admin
=
True
)
return
run_adhoc_object
(
adhoc
)
@shared_task
def
push_system_user_period
():
logger
.
debug
(
"Push system user period"
)
for
s
in
SystemUser
.
objects
.
filter
(
auto_push
=
True
):
assets
=
s
.
assets
.
all
()
name
=
PUSH_SYSTEM_USER_PERIOD_TASK_NAME
.
format
(
s
.
name
)
push_system_user
(
s
,
assets
,
name
)
def
push_system_user_to_assets_if_need
(
system_user
,
assets
=
None
,
asset_groups
=
None
):
assets_to_push
=
[]
system_user_assets
=
system_user
.
assets
.
all
()
if
assets
:
assets_to_push
.
extend
(
assets
)
if
asset_groups
:
for
group
in
asset_groups
:
assets_to_push
.
extend
(
group
.
assets
.
all
())
assets_need_push
=
set
(
assets_to_push
)
-
set
(
system_user_assets
)
if
not
assets_need_push
:
return
logger
.
debug
(
"Push system user {} to {} assets"
.
format
(
system_user
.
name
,
', '
.
join
([
asset
.
hostname
for
asset
in
assets_need_push
])
))
result
=
push_system_user
(
system_user
,
assets_need_push
,
PUSH_SYSTEM_USER_TASK_NAME
)
system_user
.
assets
.
add
(
*
tuple
(
assets_need_push
))
return
result
apps/assets/templates/assets/admin_user_assets.html
0 → 100644
View file @
0c9e24dc
This diff is collapsed.
Click to expand it.
apps/assets/templates/assets/admin_user_detail.html
View file @
0c9e24dc
This diff is collapsed.
Click to expand it.
apps/assets/templates/assets/admin_user_list.html
View file @
0c9e24dc
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
<th
class=
"text-center"
>
{% trans 'Name' %}
</th>
<th
class=
"text-center"
>
{% trans 'Name' %}
</th>
<th
class=
"text-center"
>
{% trans 'Username' %}
</th>
<th
class=
"text-center"
>
{% trans 'Username' %}
</th>
<th
class=
"text-center"
>
{% trans 'Asset num' %}
</th>
<th
class=
"text-center"
>
{% trans 'Asset num' %}
</th>
<th
class=
"text-center"
>
{% trans 'Unreachable' %}
</th>
<th
class=
"text-center"
>
{% trans 'Comment' %}
</th>
<th
class=
"text-center"
>
{% trans 'Comment' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
</tr>
</tr>
...
@@ -34,11 +35,7 @@ $(document).ready(function(){
...
@@ -34,11 +35,7 @@ $(document).ready(function(){
var
detail_btn
=
'<a href="{% url "assets:admin-user-detail" pk=99991937 %}">'
+
cellData
+
'</a>'
;
var
detail_btn
=
'<a href="{% url "assets:admin-user-detail" pk=99991937 %}">'
+
cellData
+
'</a>'
;
$
(
td
).
html
(
detail_btn
.
replace
(
'99991937'
,
rowData
.
id
));
$
(
td
).
html
(
detail_btn
.
replace
(
'99991937'
,
rowData
.
id
));
}},
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
{
targets
:
6
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
innerHtml
=
cellData
.
length
>
8
?
cellData
.
substring
(
0
,
24
)
+
'...'
:
cellData
;
$
(
td
).
html
(
'<a href="javascript:void(0);" data-toggle="tooltip" title="'
+
cellData
+
'">'
+
innerHtml
+
'</a>'
);
}},
{
targets
:
5
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
{
#
var
script_btn
=
'<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'
.
replace
(
'99991937'
,
cellData
);
#
}
{
#
var
script_btn
=
'<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'
.
replace
(
'99991937'
,
cellData
);
#
}
var
update_btn
=
'<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'99991937'
,
cellData
);
var
update_btn
=
'<a href="{% url "assets:admin-user-update" pk=99991937 %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'
.
replace
(
'99991937'
,
cellData
);
var
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'
.
replace
(
'99991937'
,
cellData
);
var
del_btn
=
'<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'
.
replace
(
'99991937'
,
cellData
);
...
@@ -47,7 +44,7 @@ $(document).ready(function(){
...
@@ -47,7 +44,7 @@ $(document).ready(function(){
}}],
}}],
ajax_url
:
'{% url "api-assets:admin-user-list" %}'
,
ajax_url
:
'{% url "api-assets:admin-user-list" %}'
,
columns
:
[{
data
:
function
(){
return
""
}},
{
data
:
"name"
},
{
data
:
"username"
},
{
data
:
"assets_amount"
},
columns
:
[{
data
:
function
(){
return
""
}},
{
data
:
"name"
},
{
data
:
"username"
},
{
data
:
"assets_amount"
},
{
data
:
"
comment"
},
{
data
:
"id"
}],
{
data
:
"
unreachable_amount"
},
{
data
:
"comment"
},
{
data
:
"id"
}]
};
};
jumpserver
.
initDataTable
(
options
);
jumpserver
.
initDataTable
(
options
);
})
})
...
...
apps/assets/templates/assets/system_user_asset.html
View file @
0c9e24dc
...
@@ -58,18 +58,16 @@
...
@@ -58,18 +58,16 @@
<td>
{{ asset.ip }}
</td>
<td>
{{ asset.ip }}
</td>
<td>
{{ asset.port }}
</td>
<td>
{{ asset.port }}
</td>
<td>
<td>
<i
class=
"fa fa-check text-navy"
></i>
<i
class=
"fa fa-check text-navy"
></i>
</td>
</td>
<td>
<td>
<button
class=
"btn btn-danger pull-right btn-xs
{% if asset.is_inherit_from_asset_groups %} disabled {% endif %}
"
type=
"button"
><i
class=
"fa fa-minus"
></i></button>
<button
class=
"btn btn-danger pull-right btn-xs"
type=
"button"
><i
class=
"fa fa-minus"
></i></button>
</td>
</td>
</tr>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</tbody>
</table>
</table>
{#
<div
class=
"row"
>
#}
{# {% include '_pagination.html' %}#}
{#
</div>
#}
</div>
</div>
</div>
</div>
</div>
</div>
...
...
apps/assets/templates/assets/system_user_list.html
View file @
0c9e24dc
...
@@ -60,7 +60,7 @@ $(document).ready(function(){
...
@@ -60,7 +60,7 @@ $(document).ready(function(){
$
(
td
).
html
(
update_btn
+
del_btn
)
$
(
td
).
html
(
update_btn
+
del_btn
)
}}],
}}],
ajax_url
:
'{% url "api-assets:system-user-list" %}'
,
ajax_url
:
'{% url "api-assets:system-user-list" %}'
,
columns
:
[{
data
:
"id"
},
{
data
:
"name"
},
{
data
:
"username"
},
{
data
:
"assets_amount"
},
{
data
:
function
()
{
return
"3"
}
},
columns
:
[{
data
:
"id"
},
{
data
:
"name"
},
{
data
:
"username"
},
{
data
:
"assets_amount"
},
{
data
:
"unreachable_amount"
},
{
data
:
"comment"
},
{
data
:
"id"
}],
{
data
:
"comment"
},
{
data
:
"id"
}],
op_html
:
$
(
'#actions'
).
html
()
op_html
:
$
(
'#actions'
).
html
()
};
};
...
...
apps/assets/urls/views_urls.py
View file @
0c9e24dc
...
@@ -41,6 +41,7 @@ urlpatterns = [
...
@@ -41,6 +41,7 @@ urlpatterns = [
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/$'
,
views
.
AdminUserDetailView
.
as_view
(),
name
=
'admin-user-detail'
),
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/$'
,
views
.
AdminUserDetailView
.
as_view
(),
name
=
'admin-user-detail'
),
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/update/$'
,
views
.
AdminUserUpdateView
.
as_view
(),
name
=
'admin-user-update'
),
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/update/$'
,
views
.
AdminUserUpdateView
.
as_view
(),
name
=
'admin-user-update'
),
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/delete/$'
,
views
.
AdminUserDeleteView
.
as_view
(),
name
=
'admin-user-delete'
),
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/delete/$'
,
views
.
AdminUserDeleteView
.
as_view
(),
name
=
'admin-user-delete'
),
url
(
r'^admin-user/(?P<pk>[0-9a-zA-Z\-]+)/assets/$'
,
views
.
AdminUserAssetsView
.
as_view
(),
name
=
'admin-user-assets'
),
# Resource system user url
# Resource system user url
url
(
r'^system-user/$'
,
views
.
SystemUserListView
.
as_view
(),
name
=
'system-user-list'
),
url
(
r'^system-user/$'
,
views
.
SystemUserListView
.
as_view
(),
name
=
'system-user-list'
),
...
...
apps/assets/utils.py
View file @
0c9e24dc
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
#
#
from
.models
import
Asset
from
common.utils
import
get_object_or_none
from
.models
import
Asset
,
SystemUser
def
test_admin_user_connective_manual
(
asset
):
from
ops.utils
import
run_AdHoc
if
not
isinstance
(
asset
,
list
):
asset
=
[
asset
]
task_tuple
=
(
(
'ping'
,
''
),
)
summary
,
_
=
run_AdHoc
(
task_tuple
,
asset
,
record
=
False
)
if
len
(
summary
[
'failed'
])
!=
0
:
return
False
else
:
return
True
def
get_assets_by_id_list
(
id_list
):
def
get_assets_by_id_list
(
id_list
):
...
@@ -25,26 +12,6 @@ def get_assets_by_hostname_list(hostname_list):
...
@@ -25,26 +12,6 @@ def get_assets_by_hostname_list(hostname_list):
return
Asset
.
objects
.
filter
(
hostname__in
=
hostname_list
)
return
Asset
.
objects
.
filter
(
hostname__in
=
hostname_list
)
def
get_asset_admin_user
(
user
,
asset
):
def
get_system_user_by_name
(
name
):
if
user
.
is_superuser
:
system_user
=
get_object_or_none
(
SystemUser
,
name
=
name
)
return
asset
.
admin_user
return
system_user
else
:
msg
=
"{} have no permission for admin user"
.
format
(
user
.
username
)
raise
PermissionError
(
msg
)
def
get_asset_system_user
(
user
,
asset
,
system_user_name
):
from
perms.utils
import
get_user_granted_assets
assets
=
get_user_granted_assets
(
user
)
system_users
=
{
system_user
.
name
:
system_user
for
system_user
in
assets
.
get
(
asset
)}
if
system_user_name
in
system_users
:
return
system_users
[
system_user_name
]
else
:
msg
=
"{} have no permission for {}"
.
format
(
user
.
name
,
system_user_name
)
raise
PermissionError
(
msg
)
def
get_assets_with_admin_by_hostname_list
(
hostname_list
):
assets
=
Asset
.
objects
.
filter
(
hostname__in
=
hostname_list
)
return
[(
asset
,
asset
.
admin_user
)
for
asset
in
assets
]
apps/assets/views/admin_user.py
View file @
0c9e24dc
...
@@ -14,7 +14,7 @@ from ..hands import AdminUserRequiredMixin
...
@@ -14,7 +14,7 @@ from ..hands import AdminUserRequiredMixin
__all__
=
[
'AdminUserCreateView'
,
'AdminUserDetailView'
,
__all__
=
[
'AdminUserCreateView'
,
'AdminUserDetailView'
,
'AdminUserDeleteView'
,
'AdminUserListView'
,
'AdminUserDeleteView'
,
'AdminUserListView'
,
'AdminUserUpdateView'
,
'AdminUserUpdateView'
,
'AdminUserAssetsView'
,
]
]
...
@@ -104,6 +104,31 @@ class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
...
@@ -104,6 +104,31 @@ class AdminUserDetailView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
return
super
(
AdminUserDetailView
,
self
)
.
get_context_data
(
**
kwargs
)
return
super
(
AdminUserDetailView
,
self
)
.
get_context_data
(
**
kwargs
)
class
AdminUserAssetsView
(
AdminUserRequiredMixin
,
SingleObjectMixin
,
ListView
):
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
template_name
=
'assets/admin_user_assets.html'
context_object_name
=
'admin_user'
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
object
=
self
.
get_object
(
queryset
=
AdminUser
.
objects
.
all
())
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
def
get_queryset
(
self
):
self
.
queryset
=
self
.
object
.
assets
.
all
()
sorted
(
self
.
queryset
,
key
=
lambda
x
:
x
.
is_connective
()
is
False
)
return
self
.
queryset
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
'assets'
,
'action'
:
'Admin user detail'
,
"total_amount"
:
len
(
self
.
queryset
),
'unreachable_amount'
:
len
([
asset
for
asset
in
self
.
queryset
if
asset
.
is_connective
()
is
False
])
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
AdminUserDeleteView
(
AdminUserRequiredMixin
,
DeleteView
):
class
AdminUserDeleteView
(
AdminUserRequiredMixin
,
DeleteView
):
model
=
AdminUser
model
=
AdminUser
template_name
=
'assets/delete_confirm.html'
template_name
=
'assets/delete_confirm.html'
...
...
apps/assets/views/system_user.py
View file @
0c9e24dc
...
@@ -117,25 +117,18 @@ class SystemUserAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
...
@@ -117,25 +117,18 @@ class SystemUserAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
object
=
self
.
get_object
(
queryset
=
SystemUser
.
objects
.
all
())
self
.
object
=
self
.
get_object
(
queryset
=
SystemUser
.
objects
.
all
())
return
super
(
SystemUserAssetView
,
self
)
.
get
(
request
,
*
args
,
**
kwargs
)
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
def
get_asset_groups
(
self
):
return
self
.
object
.
asset_groups
.
all
()
# Todo: queryset default order by connectivity, need ops support
def
get_queryset
(
self
):
def
get_queryset
(
self
):
return
list
(
self
.
object
.
get_assets
()
)
return
self
.
object
.
assets
.
all
(
)
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
asset_groups
=
self
.
get_asset_groups
()
assets
=
self
.
get_queryset
()
assets
=
self
.
get_queryset
()
context
=
{
context
=
{
'app'
:
'assets'
,
'app'
:
'assets'
,
'action'
:
'System user asset'
,
'action'
:
'System user asset'
,
'assets_remain'
:
[
asset
for
asset
in
Asset
.
objects
.
all
()
if
asset
not
in
assets
],
'assets_remain'
:
[
asset
for
asset
in
Asset
.
objects
.
all
()
if
asset
not
in
assets
],
'asset_groups'
:
asset_groups
,
'asset_groups'
:
AssetGroup
.
objects
.
all
(),
'asset_groups_remain'
:
[
asset_group
for
asset_group
in
AssetGroup
.
objects
.
all
()
if
asset_group
not
in
asset_groups
]
}
}
kwargs
.
update
(
context
)
kwargs
.
update
(
context
)
return
super
(
SystemUserAssetView
,
self
)
.
get_context_data
(
**
kwargs
)
return
super
(
SystemUserAssetView
,
self
)
.
get_context_data
(
**
kwargs
)
...
...
apps/common/celery.py
View file @
0c9e24dc
...
@@ -27,8 +27,8 @@ app.conf.update(
...
@@ -27,8 +27,8 @@ app.conf.update(
'schedule'
:
60
*
60
*
60
*
24
,
'schedule'
:
60
*
60
*
60
*
24
,
'args'
:
(),
'args'
:
(),
},
},
'test-admin-user-connect
iv
e'
:
{
'test-admin-user-connect
ability_period
e'
:
{
'task'
:
'assets.tasks.test_admin_user_connect
ive
_period'
,
'task'
:
'assets.tasks.test_admin_user_connect
ability
_period'
,
'schedule'
:
60
*
60
*
60
,
'schedule'
:
60
*
60
*
60
,
'args'
:
(),
'args'
:
(),
},
},
...
...
apps/common/utils.py
View file @
0c9e24dc
...
@@ -15,6 +15,7 @@ from email.utils import formatdate
...
@@ -15,6 +15,7 @@ from email.utils import formatdate
import
calendar
import
calendar
import
threading
import
threading
from
six
import
StringIO
from
six
import
StringIO
import
uuid
import
paramiko
import
paramiko
import
sshpubkeys
import
sshpubkeys
...
@@ -378,4 +379,8 @@ def sum_capacity(cap_list):
...
@@ -378,4 +379,8 @@ def sum_capacity(cap_list):
return
capacity_convert
(
total
,
expect
=
'auto'
)
return
capacity_convert
(
total
,
expect
=
'auto'
)
def
get_short_uuid_str
():
return
str
(
uuid
.
uuid4
())
.
split
(
'-'
)[
-
1
]
signer
=
Signer
()
signer
=
Signer
()
apps/jumpserver/settings.py
View file @
0c9e24dc
...
@@ -299,6 +299,7 @@ REST_FRAMEWORK = {
...
@@ -299,6 +299,7 @@ REST_FRAMEWORK = {
'users.authentication.SessionAuthentication'
,
'users.authentication.SessionAuthentication'
,
),
),
'DEFAULT_FILTER_BACKENDS'
:
(
'django_filters.rest_framework.DjangoFilterBackend'
,),
'DEFAULT_FILTER_BACKENDS'
:
(
'django_filters.rest_framework.DjangoFilterBackend'
,),
'DATETIME_FORMAT'
:
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
,
}
}
AUTHENTICATION_BACKENDS
=
[
AUTHENTICATION_BACKENDS
=
[
...
...
apps/ops/ansible/callback.py
View file @
0c9e24dc
...
@@ -5,21 +5,21 @@ from ansible.plugins.callback import CallbackBase
...
@@ -5,21 +5,21 @@ from ansible.plugins.callback import CallbackBase
class
AdHocResultCallback
(
CallbackBase
):
class
AdHocResultCallback
(
CallbackBase
):
"""
"""
AdHoc
result Callback
Task
result Callback
"""
"""
def
__init__
(
self
,
display
=
None
):
def
__init__
(
self
,
display
=
None
):
# result_raw example: {
# result_raw example: {
# "ok": {"hostname":
[{"task_name": {},...]
,..},
# "ok": {"hostname":
{"task_name": {},...}
,..},
# "failed": {"hostname":
["task_name": {}..]
, ..},
# "failed": {"hostname":
{"task_name": {}..}
, ..},
# "unreachable: {"hostname":
["task_name": {}, ..]
},
# "unreachable: {"hostname":
{"task_name": {}, ..}
},
# "skipped": {"hostname":
["task_name": {}, ..]
, ..},
# "skipped": {"hostname":
{"task_name": {}, ..}
, ..},
# }
# }
# results_summary example: {
# results_summary example: {
# "contacted": {"hostname",...},
# "contacted": {"hostname",...},
# "dark": {"hostname":
[{"task_name": "error"},...]
,},
# "dark": {"hostname":
{"task_name": {}, "task_name": {}},...
,},
# }
# }
self
.
results_raw
=
dict
(
ok
=
{},
failed
=
{},
unreachable
=
{},
skipped
=
{})
self
.
results_raw
=
dict
(
ok
=
{},
failed
=
{},
unreachable
=
{},
skipped
=
{})
self
.
results_summary
=
dict
(
contacted
=
set
()
,
dark
=
{})
self
.
results_summary
=
dict
(
contacted
=
[]
,
dark
=
{})
super
()
.
__init__
(
display
)
super
()
.
__init__
(
display
)
def
gather_result
(
self
,
t
,
res
):
def
gather_result
(
self
,
t
,
res
):
...
@@ -28,23 +28,24 @@ class AdHocResultCallback(CallbackBase):
...
@@ -28,23 +28,24 @@ class AdHocResultCallback(CallbackBase):
task_result
=
res
.
_result
task_result
=
res
.
_result
if
self
.
results_raw
[
t
]
.
get
(
host
):
if
self
.
results_raw
[
t
]
.
get
(
host
):
self
.
results_raw
[
t
][
host
]
.
append
({
task_name
:
task_result
})
self
.
results_raw
[
t
][
host
]
[
task_name
]
=
task_result
else
:
else
:
self
.
results_raw
[
t
][
host
]
=
[{
task_name
:
task_result
}]
self
.
results_raw
[
t
][
host
]
=
{
task_name
:
task_result
}
self
.
clean_result
(
t
,
host
,
task_name
,
task_result
)
self
.
clean_result
(
t
,
host
,
task_name
,
task_result
)
def
clean_result
(
self
,
t
,
host
,
task_name
,
task_result
):
def
clean_result
(
self
,
t
,
host
,
task_name
,
task_result
):
contacted
=
self
.
results_summary
[
"contacted"
]
contacted
=
self
.
results_summary
[
"contacted"
]
dark
=
self
.
results_summary
[
"dark"
]
dark
=
self
.
results_summary
[
"dark"
]
if
t
in
(
"ok"
,
"skipped"
)
and
host
not
in
dark
:
if
t
in
(
"ok"
,
"skipped"
)
and
host
not
in
dark
:
contacted
.
add
(
host
)
if
host
not
in
contacted
:
contacted
.
append
(
host
)
else
:
else
:
if
dark
.
get
(
host
):
if
dark
.
get
(
host
):
dark
[
host
]
.
append
({
task_name
:
task_result
})
dark
[
host
]
[
task_name
]
=
task_result
else
:
else
:
dark
[
host
]
=
[{
task_name
:
task_result
}]
dark
[
host
]
=
{
task_name
:
task_result
}
if
host
in
contacted
:
if
host
in
contacted
:
contacted
.
remove
(
dark
)
contacted
.
remove
(
host
)
def
v2_runner_on_failed
(
self
,
result
,
ignore_errors
=
False
):
def
v2_runner_on_failed
(
self
,
result
,
ignore_errors
=
False
):
self
.
gather_result
(
"failed"
,
result
)
self
.
gather_result
(
"failed"
,
result
)
...
...
apps/ops/ansible/inventory.py
View file @
0c9e24dc
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
from
ansible.inventory.group
import
Group
from
ansible.inventory.host
import
Host
from
ansible.inventory.host
import
Host
from
ansible.vars.manager
import
VariableManager
from
ansible.vars.manager
import
VariableManager
from
ansible.inventory.manager
import
InventoryManager
from
ansible.inventory.manager
import
InventoryManager
from
ansible.parsing.dataloader
import
DataLoader
from
ansible.parsing.dataloader
import
DataLoader
class
JMSHost
(
Host
):
__all__
=
[
'BaseHost'
,
'BaseInventory'
]
class
BaseHost
(
Host
):
def
__init__
(
self
,
host_data
):
def
__init__
(
self
,
host_data
):
"""
"""
初始化
初始化
...
@@ -14,6 +18,7 @@ class JMSHost(Host):
...
@@ -14,6 +18,7 @@ class JMSHost(Host):
"hostname": "",
"hostname": "",
"ip": "",
"ip": "",
"port": "",
"port": "",
# behind is not must be required
"username": "",
"username": "",
"password": "",
"password": "",
"private_key": "",
"private_key": "",
...
@@ -29,7 +34,7 @@ class JMSHost(Host):
...
@@ -29,7 +34,7 @@ class JMSHost(Host):
self
.
host_data
=
host_data
self
.
host_data
=
host_data
hostname
=
host_data
.
get
(
'hostname'
)
or
host_data
.
get
(
'ip'
)
hostname
=
host_data
.
get
(
'hostname'
)
or
host_data
.
get
(
'ip'
)
port
=
host_data
.
get
(
'port'
)
or
22
port
=
host_data
.
get
(
'port'
)
or
22
super
(
JMSHost
,
self
)
.
__init__
(
hostname
,
port
)
super
()
.
__init__
(
hostname
,
port
)
self
.
__set_required_variables
()
self
.
__set_required_variables
()
self
.
__set_extra_variables
()
self
.
__set_extra_variables
()
...
@@ -37,7 +42,9 @@ class JMSHost(Host):
...
@@ -37,7 +42,9 @@ class JMSHost(Host):
host_data
=
self
.
host_data
host_data
=
self
.
host_data
self
.
set_variable
(
'ansible_host'
,
host_data
[
'ip'
])
self
.
set_variable
(
'ansible_host'
,
host_data
[
'ip'
])
self
.
set_variable
(
'ansible_port'
,
host_data
[
'port'
])
self
.
set_variable
(
'ansible_port'
,
host_data
[
'port'
])
self
.
set_variable
(
'ansible_user'
,
host_data
[
'username'
])
if
host_data
.
get
(
'username'
):
self
.
set_variable
(
'ansible_user'
,
host_data
[
'username'
])
# 添加密码和秘钥
# 添加密码和秘钥
if
host_data
.
get
(
'password'
):
if
host_data
.
get
(
'password'
):
...
@@ -63,30 +70,15 @@ class JMSHost(Host):
...
@@ -63,30 +70,15 @@ class JMSHost(Host):
return
self
.
name
return
self
.
name
class
JMS
Inventory
(
InventoryManager
):
class
Base
Inventory
(
InventoryManager
):
"""
"""
提供生成Ansible inventory对象的方法
提供生成Ansible inventory对象的方法
"""
"""
loader_class
=
DataLoader
loader_class
=
DataLoader
variable_manager_class
=
VariableManager
variable_manager_class
=
VariableManager
host_manager_class
=
JMS
Host
host_manager_class
=
Base
Host
def
__init__
(
self
,
host_list
=
None
):
def
__init__
(
self
,
host_list
=
None
):
if
host_list
is
None
:
host_list
=
[]
self
.
host_list
=
host_list
assert
isinstance
(
host_list
,
list
)
self
.
loader
=
self
.
loader_class
()
self
.
variable_manager
=
self
.
variable_manager_class
()
super
()
.
__init__
(
self
.
loader
)
def
get_groups
(
self
):
return
self
.
_inventory
.
groups
def
get_group
(
self
,
name
):
return
self
.
_inventory
.
groups
.
get
(
name
,
None
)
def
parse_sources
(
self
,
cache
=
False
):
"""
"""
用于生成动态构建Ansible Inventory. super().__init__ 会自动调用
用于生成动态构建Ansible Inventory. super().__init__ 会自动调用
host_list: [{
host_list: [{
...
@@ -105,9 +97,23 @@ class JMSInventory(InventoryManager):
...
@@ -105,9 +97,23 @@ class JMSInventory(InventoryManager):
"vars": {},
"vars": {},
},
},
]
]
:param host_list:
:return: None
"""
"""
if
host_list
is
None
:
host_list
=
[]
self
.
host_list
=
host_list
assert
isinstance
(
host_list
,
list
)
self
.
loader
=
self
.
loader_class
()
self
.
variable_manager
=
self
.
variable_manager_class
()
super
()
.
__init__
(
self
.
loader
)
def
get_groups
(
self
):
return
self
.
_inventory
.
groups
def
get_group
(
self
,
name
):
return
self
.
_inventory
.
groups
.
get
(
name
,
None
)
def
parse_sources
(
self
,
cache
=
False
):
group_all
=
self
.
get_group
(
'all'
)
group_all
=
self
.
get_group
(
'all'
)
ungrouped
=
self
.
get_group
(
'ungrouped'
)
ungrouped
=
self
.
get_group
(
'ungrouped'
)
...
@@ -119,9 +125,14 @@ class JMSInventory(InventoryManager):
...
@@ -119,9 +125,14 @@ class JMSInventory(InventoryManager):
for
group_name
in
groups_data
:
for
group_name
in
groups_data
:
group
=
self
.
get_group
(
group_name
)
group
=
self
.
get_group
(
group_name
)
if
group
is
None
:
if
group
is
None
:
group
=
G
roup
(
group_name
)
self
.
add_g
roup
(
group_name
)
self
.
add_group
(
group
)
group
=
self
.
get_group
(
group_name
)
group
.
add_host
(
host
)
group
.
add_host
(
host
)
else
:
else
:
ungrouped
.
add_host
(
host
)
ungrouped
.
add_host
(
host
)
group_all
.
add_host
(
host
)
group_all
.
add_host
(
host
)
def
get_matched_hosts
(
self
,
pattern
):
return
self
.
get_hosts
(
pattern
)
apps/ops/ansible/runner.py
View file @
0c9e24dc
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
import
os
import
os
from
collections
import
namedtuple
from
collections
import
namedtuple
...
@@ -11,7 +10,6 @@ from ansible.executor.playbook_executor import PlaybookExecutor
...
@@ -11,7 +10,6 @@ from ansible.executor.playbook_executor import PlaybookExecutor
from
ansible.playbook.play
import
Play
from
ansible.playbook.play
import
Play
import
ansible.constants
as
C
import
ansible.constants
as
C
from
.inventory
import
JMSInventory
from
.callback
import
AdHocResultCallback
,
PlaybookResultCallBack
,
\
from
.callback
import
AdHocResultCallback
,
PlaybookResultCallBack
,
\
CommandResultCallback
CommandResultCallback
from
common.utils
import
get_logger
from
common.utils
import
get_logger
...
@@ -71,36 +69,19 @@ class PlayBookRunner:
...
@@ -71,36 +69,19 @@ class PlayBookRunner:
# Default results callback
# Default results callback
results_callback_class
=
PlaybookResultCallBack
results_callback_class
=
PlaybookResultCallBack
inventory_class
=
JMSInventory
loader_class
=
DataLoader
loader_class
=
DataLoader
variable_manager_class
=
VariableManager
variable_manager_class
=
VariableManager
options
=
get_default_options
()
options
=
get_default_options
()
def
__init__
(
self
,
hosts
=
None
,
options
=
None
):
def
__init__
(
self
,
inventory
=
None
,
options
=
None
):
"""
"""
:param options: Ansible options like ansible.cfg
:param options: Ansible options like ansible.cfg
:param hosts: [
:param inventory: Ansible inventory
{
"hostname": "",
"ip": "",
"port": "",
"username": "",
"password": "",
"private_key": "",
"become": {
"method": "",
"user": "",
"pass": "",
},
"groups": [],
"vars": {},
},
]
"""
"""
if
options
:
if
options
:
self
.
options
=
options
self
.
options
=
options
C
.
RETRY_FILES_ENABLED
=
False
C
.
RETRY_FILES_ENABLED
=
False
self
.
inventory
=
self
.
inventory_class
(
hosts
)
self
.
inventory
=
inventory
self
.
loader
=
self
.
loader_class
()
self
.
loader
=
self
.
loader_class
()
self
.
results_callback
=
self
.
results_callback_class
()
self
.
results_callback
=
self
.
results_callback_class
()
self
.
playbook_path
=
options
.
playbook_path
self
.
playbook_path
=
options
.
playbook_path
...
@@ -141,20 +122,19 @@ class AdHocRunner:
...
@@ -141,20 +122,19 @@ class AdHocRunner:
ADHoc Runner接口
ADHoc Runner接口
"""
"""
results_callback_class
=
AdHocResultCallback
results_callback_class
=
AdHocResultCallback
inventory_class
=
JMSInventory
loader_class
=
DataLoader
loader_class
=
DataLoader
variable_manager_class
=
VariableManager
variable_manager_class
=
VariableManager
options
=
get_default_options
()
options
=
get_default_options
()
default_options
=
get_default_options
()
default_options
=
get_default_options
()
def
__init__
(
self
,
hosts
,
options
=
None
):
def
__init__
(
self
,
inventory
,
options
=
None
):
if
options
:
if
options
:
self
.
options
=
options
self
.
options
=
options
self
.
inventory
=
inventory
self
.
pattern
=
''
self
.
loader
=
DataLoader
()
self
.
loader
=
DataLoader
()
self
.
inventory
=
self
.
inventory_class
(
hosts
)
self
.
variable_manager
=
VariableManager
(
self
.
variable_manager
=
VariableManager
(
loader
=
self
.
loader
,
inventory
=
self
.
inventory
)
loader
=
self
.
loader
,
inventory
=
self
.
inventory
)
@staticmethod
@staticmethod
def
check_module_args
(
module_name
,
module_args
=
''
):
def
check_module_args
(
module_name
,
module_args
=
''
):
...
@@ -163,14 +143,22 @@ class AdHocRunner:
...
@@ -163,14 +143,22 @@ class AdHocRunner:
raise
AnsibleError
(
err
)
raise
AnsibleError
(
err
)
def
check_pattern
(
self
,
pattern
):
def
check_pattern
(
self
,
pattern
):
if
not
pattern
:
raise
AnsibleError
(
"Pattern `{}` is not valid!"
.
format
(
pattern
))
if
not
self
.
inventory
.
list_hosts
(
"all"
):
if
not
self
.
inventory
.
list_hosts
(
"all"
):
raise
AnsibleError
(
"Inventory is empty."
)
raise
AnsibleError
(
"Inventory is empty."
)
if
not
self
.
inventory
.
list_hosts
(
pattern
):
if
not
self
.
inventory
.
list_hosts
(
pattern
):
raise
AnsibleError
(
raise
AnsibleError
(
"pattern:
%
s dose not match any hosts."
%
pattern
"pattern:
%
s dose not match any hosts."
%
pattern
)
)
def
clean_tasks
(
self
,
tasks
):
cleaned_tasks
=
[]
for
task
in
tasks
:
self
.
check_module_args
(
task
[
'action'
][
'module'
],
task
[
'action'
]
.
get
(
'args'
))
cleaned_tasks
.
append
(
task
)
return
cleaned_tasks
def
set_option
(
self
,
k
,
v
):
def
set_option
(
self
,
k
,
v
):
kwargs
=
{
k
:
v
}
kwargs
=
{
k
:
v
}
self
.
options
=
self
.
options
.
_replace
(
**
kwargs
)
self
.
options
=
self
.
options
.
_replace
(
**
kwargs
)
...
@@ -182,17 +170,15 @@ class AdHocRunner:
...
@@ -182,17 +170,15 @@ class AdHocRunner:
:param play_name: The play name
:param play_name: The play name
:return:
:return:
"""
"""
self
.
check_pattern
(
pattern
)
results_callback
=
self
.
results_callback_class
()
results_callback
=
self
.
results_callback_class
()
clean_tasks
=
[]
cleaned_tasks
=
self
.
clean_tasks
(
tasks
)
for
task
in
tasks
:
self
.
check_module_args
(
task
[
'action'
][
'module'
],
task
[
'action'
]
.
get
(
'args'
))
clean_tasks
.
append
(
task
)
play_source
=
dict
(
play_source
=
dict
(
name
=
play_name
,
name
=
play_name
,
hosts
=
pattern
,
hosts
=
pattern
,
gather_facts
=
gather_facts
,
gather_facts
=
gather_facts
,
tasks
=
clean_tasks
tasks
=
clean
ed
_tasks
)
)
play
=
Play
()
.
load
(
play
=
Play
()
.
load
(
...
@@ -209,6 +195,9 @@ class AdHocRunner:
...
@@ -209,6 +195,9 @@ class AdHocRunner:
stdout_callback
=
results_callback
,
stdout_callback
=
results_callback
,
passwords
=
self
.
options
.
passwords
,
passwords
=
self
.
options
.
passwords
,
)
)
logger
.
debug
(
"Get inventory matched hosts: {}"
.
format
(
self
.
inventory
.
get_matched_hosts
(
pattern
)
))
try
:
try
:
tqm
.
run
(
play
)
tqm
.
run
(
play
)
...
...
apps/ops/ansible/test_inventory.
bak
→
apps/ops/ansible/test_inventory.
py
View file @
0c9e24dc
...
@@ -6,7 +6,7 @@ import unittest
...
@@ -6,7 +6,7 @@ import unittest
sys
.
path
.
insert
(
0
,
'../..'
)
sys
.
path
.
insert
(
0
,
'../..'
)
from ops.ansible.inventory import
JMS
Inventory
from
ops.ansible.inventory
import
Base
Inventory
class
TestJMSInventory
(
unittest
.
TestCase
):
class
TestJMSInventory
(
unittest
.
TestCase
):
...
@@ -41,7 +41,7 @@ class TestJMSInventory(unittest.TestCase):
...
@@ -41,7 +41,7 @@ class TestJMSInventory(unittest.TestCase):
"vars"
:
{
"love"
:
"yes"
},
"vars"
:
{
"love"
:
"yes"
},
}]
}]
self.inventory =
JMS
Inventory(host_list=host_list)
self
.
inventory
=
Base
Inventory
(
host_list
=
host_list
)
def
test_hosts
(
self
):
def
test_hosts
(
self
):
print
(
"#"
*
10
+
"Hosts"
+
"#"
*
10
)
print
(
"#"
*
10
+
"Hosts"
+
"#"
*
10
)
...
...
apps/ops/ansible/test_runner.
bak
→
apps/ops/ansible/test_runner.
py
View file @
0c9e24dc
...
@@ -7,6 +7,7 @@ import sys
...
@@ -7,6 +7,7 @@ import sys
sys
.
path
.
insert
(
0
,
"../.."
)
sys
.
path
.
insert
(
0
,
"../.."
)
from
ops.ansible.runner
import
AdHocRunner
,
CommandRunner
from
ops.ansible.runner
import
AdHocRunner
,
CommandRunner
from
ops.ansible.inventory
import
BaseInventory
class
TestAdHocRunner
(
unittest
.
TestCase
):
class
TestAdHocRunner
(
unittest
.
TestCase
):
...
@@ -20,7 +21,8 @@ class TestAdHocRunner(unittest.TestCase):
...
@@ -20,7 +21,8 @@ class TestAdHocRunner(unittest.TestCase):
"password"
:
"redhat"
,
"password"
:
"redhat"
,
},
},
]
]
self.runner = AdHocRunner(hosts=host_data)
inventory
=
BaseInventory
(
host_data
)
self
.
runner
=
AdHocRunner
(
inventory
)
def
test_run
(
self
):
def
test_run
(
self
):
tasks
=
[
tasks
=
[
...
@@ -43,7 +45,8 @@ class TestCommandRunner(unittest.TestCase):
...
@@ -43,7 +45,8 @@ class TestCommandRunner(unittest.TestCase):
"password"
:
"redhat"
,
"password"
:
"redhat"
,
},
},
]
]
self.runner = CommandRunner(hosts=host_data)
inventory
=
BaseInventory
(
host_data
)
self
.
runner
=
CommandRunner
(
inventory
)
def
test_execute
(
self
):
def
test_execute
(
self
):
res
=
self
.
runner
.
execute
(
'ls'
,
'all'
)
res
=
self
.
runner
.
execute
(
'ls'
,
'all'
)
...
...
apps/ops/api.py
View file @
0c9e24dc
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
from
django.shortcuts
import
get_object_or_404
from
rest_framework
import
viewsets
from
rest_framework
import
viewsets
from
.hands
import
IsSuperUser
from
.hands
import
IsSuperUser
from
.models
import
AdHoc
from
.models
import
Task
,
AdHoc
,
AdHocRunHistory
from
.serializers
import
TaskSerializer
from
.serializers
import
TaskSerializer
,
AdHocSerializer
,
AdHocRunHistorySerializer
class
TaskViewSet
(
viewsets
.
ModelViewSet
):
class
TaskViewSet
(
viewsets
.
ModelViewSet
):
queryset
=
AdHoc
.
objects
.
all
()
queryset
=
Task
.
objects
.
all
()
serializer_class
=
TaskSerializer
serializer_class
=
TaskSerializer
permission_classes
=
(
IsSuperUser
,)
permission_classes
=
(
IsSuperUser
,)
class
AdHocViewSet
(
viewsets
.
ModelViewSet
):
queryset
=
AdHoc
.
objects
.
all
()
serializer_class
=
AdHocSerializer
permission_classes
=
(
IsSuperUser
,)
def
get_queryset
(
self
):
task_id
=
self
.
request
.
query_params
.
get
(
'task'
)
if
task_id
:
task
=
get_object_or_404
(
Task
,
id
=
task_id
)
self
.
queryset
=
self
.
queryset
.
filter
(
task
=
task
)
return
self
.
queryset
class
AdHocRunHistorySet
(
viewsets
.
ModelViewSet
):
queryset
=
AdHocRunHistory
.
objects
.
all
()
serializer_class
=
AdHocRunHistorySerializer
permission_classes
=
(
IsSuperUser
,)
def
get_queryset
(
self
):
task_id
=
self
.
request
.
query_params
.
get
(
'task'
)
if
task_id
:
task
=
get_object_or_404
(
Task
,
id
=
task_id
)
adhocs
=
task
.
adhoc
.
all
()
self
.
queryset
=
self
.
queryset
.
filter
(
adhoc__in
=
adhocs
)
return
self
.
queryset
apps/ops/inventory.py
0 → 100644
View file @
0c9e24dc
# -*- coding: utf-8 -*-
#
from
.ansible.inventory
import
BaseInventory
from
assets.utils
import
get_assets_by_hostname_list
,
get_system_user_by_name
__all__
=
[
'JMSInventory'
]
class
JMSInventory
(
BaseInventory
):
"""
JMS Inventory is the manager with jumpserver assets, so you can
write you own manager, construct you inventory
"""
def
__init__
(
self
,
hostname_list
,
run_as_admin
=
False
,
run_as
=
None
,
become_info
=
None
):
self
.
hostname_list
=
hostname_list
self
.
using_admin
=
run_as_admin
self
.
run_as
=
run_as
self
.
become_info
=
become_info
assets
=
self
.
get_jms_assets
()
if
run_as_admin
:
host_list
=
[
asset
.
_to_secret_json
()
for
asset
in
assets
]
else
:
host_list
=
[
asset
.
to_json
()
for
asset
in
assets
]
if
run_as
:
run_user_info
=
self
.
get_run_user_info
()
for
host
in
host_list
:
host
.
update
(
run_user_info
)
if
become_info
:
for
host
in
host_list
:
host
.
update
(
become_info
)
super
()
.
__init__
(
host_list
=
host_list
)
def
get_jms_assets
(
self
):
assets
=
get_assets_by_hostname_list
(
self
.
hostname_list
)
return
assets
def
get_run_user_info
(
self
):
system_user
=
get_system_user_by_name
(
self
.
run_as
)
if
not
system_user
:
return
{}
else
:
return
system_user
.
_to_secret_json
()
apps/ops/models.py
View file @
0c9e24dc
...
@@ -6,20 +6,25 @@ import uuid
...
@@ -6,20 +6,25 @@ import uuid
from
django.db
import
models
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
common.utils
import
signer
from
common.utils
import
signer
__all__
=
[
"AdHoc"
,
"AdHocRunHistory"
]
__all__
=
[
"
Task"
,
"
AdHoc"
,
"AdHocRunHistory"
]
logger
=
logging
.
getLogger
(
__name__
)
logger
=
logging
.
getLogger
(
__name__
)
class
AdHoc
(
models
.
Model
):
class
Task
(
models
.
Model
):
"""
This task is different ansible task, Task like 'push system user', 'get asset info' ..
One task can have some versions of adhoc, run a task only run the latest version adhoc
"""
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
name
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Name'
))
name
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Name'
))
is_deleted
=
models
.
BooleanField
(
default
=
False
)
is_deleted
=
models
.
BooleanField
(
default
=
False
)
created_by
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
default
=
''
)
created_by
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
default
=
''
)
date_create
=
models
.
DateTimeField
(
auto_now_add
=
True
)
date_create
d
=
models
.
DateTimeField
(
auto_now_add
=
True
)
@property
@property
def
short_id
(
self
):
def
short_id
(
self
):
...
@@ -28,24 +33,48 @@ class AdHoc(models.Model):
...
@@ -28,24 +33,48 @@ class AdHoc(models.Model):
def
__str__
(
self
):
def
__str__
(
self
):
return
self
.
name
return
self
.
name
def
get_latest_adhoc
(
self
):
return
self
.
adhoc
.
all
()
.
order_by
(
'date_created'
)
.
last
()
def
get_latest_history
(
self
):
return
self
.
get_latest_adhoc
()
.
get_latest_history
()
def
get_all_run_history
(
self
):
adhocs
=
self
.
adhoc
.
all
()
return
AdHocRunHistory
.
objects
.
filter
(
adhoc__in
=
adhocs
)
def
get_all_run_times
(
self
):
history_all
=
self
.
get_all_run_history
()
total
=
len
(
history_all
)
success
=
len
([
history
for
history
in
history_all
if
history
.
is_success
])
failed
=
len
([
history
for
history
in
history_all
if
not
history
.
is_success
])
return
{
'total'
:
total
,
'success'
:
success
,
'failed'
:
failed
}
class
AdHocData
(
models
.
Model
):
class
Meta
:
BECOME_METHOD_CHOICES
=
(
db_table
=
'ops_task'
(
'sudo'
,
'sudo'
),
(
'su'
,
'su'
),
)
class
AdHoc
(
models
.
Model
):
version
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
"""
subject
=
models
.
ForeignKey
(
AdHoc
,
on_delete
=
models
.
CASCADE
)
task: A task reference
_tasks
=
models
.
TextField
(
verbose_name
=
_
(
'Tasks'
))
# [{'name': 'task_name', 'action': {'module': '', 'args': ''}, 'other..': ''}, ]
_tasks: [{'name': 'task_name', 'action': {'module': '', 'args': ''}, 'other..': ''}, ]
_options: ansible options, more see ops.ansible.runner.Options
_hosts: ["hostname1", "hostname2"], hostname must be unique key of cmdb
run_as_admin: if true, then need get every host admin user run it, because every host may be have different admin user, so we choise host level
run_as: if not run as admin, it run it as a system/common user from cmdb
_become: May be using become [sudo, su] options. {method: "sudo", user: "user", pass: "pass"]
pattern: Even if we set _hosts, We only use that to make inventory, We also can set `patter` to run task on match hosts
"""
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
task
=
models
.
ForeignKey
(
Task
,
related_name
=
'adhoc'
,
on_delete
=
models
.
CASCADE
)
_tasks
=
models
.
TextField
(
verbose_name
=
_
(
'Tasks'
))
pattern
=
models
.
CharField
(
max_length
=
64
,
default
=
''
,
verbose_name
=
_
(
'Pattern'
))
_options
=
models
.
CharField
(
max_length
=
1024
,
default
=
''
,
verbose_name
=
_
(
'Options'
))
_hosts
=
models
.
TextField
(
blank
=
True
,
verbose_name
=
_
(
'Hosts'
))
# ['hostname1', 'hostname2']
_hosts
=
models
.
TextField
(
blank
=
True
,
verbose_name
=
_
(
'Hosts'
))
# ['hostname1', 'hostname2']
run_as_admin
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Run as admin'
))
run_as_admin
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Run as admin'
))
run_as
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
"Run as"
))
run_as
=
models
.
CharField
(
max_length
=
128
,
default
=
''
,
verbose_name
=
_
(
"Run as"
))
become
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
"Become"
))
_become
=
models
.
CharField
(
max_length
=
1024
,
default
=
''
,
verbose_name
=
_
(
"Become"
))
become_method
=
models
.
CharField
(
choices
=
BECOME_METHOD_CHOICES
,
default
=
'sudo'
,
max_length
=
4
)
created_by
=
models
.
CharField
(
max_length
=
64
,
default
=
''
,
verbose_name
=
_
(
'Create by'
))
become_user
=
models
.
CharField
(
default
=
'root'
,
max_length
=
64
)
_become_pass
=
models
.
CharField
(
default
=
''
,
max_length
=
128
)
pattern
=
models
.
CharField
(
max_length
=
64
,
default
=
''
,
verbose_name
=
_
(
'Pattern'
))
created_by
=
models
.
CharField
(
max_length
=
64
,
verbose_name
=
_
(
'Create by'
))
date_created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
date_created
=
models
.
DateTimeField
(
auto_now_add
=
True
)
@property
@property
...
@@ -54,7 +83,10 @@ class AdHocData(models.Model):
...
@@ -54,7 +83,10 @@ class AdHocData(models.Model):
@tasks.setter
@tasks.setter
def
tasks
(
self
,
item
):
def
tasks
(
self
,
item
):
self
.
_tasks
=
json
.
dumps
(
item
)
if
item
and
isinstance
(
item
,
list
):
self
.
_tasks
=
json
.
dumps
(
item
)
else
:
raise
SyntaxError
(
'Tasks should be a list'
)
@property
@property
def
hosts
(
self
):
def
hosts
(
self
):
...
@@ -65,42 +97,83 @@ class AdHocData(models.Model):
...
@@ -65,42 +97,83 @@ class AdHocData(models.Model):
self
.
_hosts
=
json
.
dumps
(
item
)
self
.
_hosts
=
json
.
dumps
(
item
)
@property
@property
def
become_pass
(
self
):
def
become
(
self
):
return
signer
.
unsign
(
self
.
_become_pass
)
if
self
.
_become
:
return
json
.
loads
(
signer
.
unsign
(
self
.
_become
))
else
:
return
{}
@become.setter
def
become
(
self
,
item
):
"""
:param item: {
method: "sudo",
user: "user",
pass: "pass",
}
:return:
"""
self
.
_become
=
signer
.
sign
(
json
.
dumps
(
item
))
@property
def
options
(
self
):
if
self
.
_options
:
return
json
.
loads
(
self
.
_options
)
else
:
return
{}
@
become_pas
s.setter
@
option
s.setter
def
become_pass
(
self
,
password
):
def
options
(
self
,
item
):
self
.
_
become_pass
=
signer
.
sign
(
password
)
self
.
_
options
=
json
.
dumps
(
item
)
@property
@property
def
short_
version
(
self
):
def
short_
id
(
self
):
return
str
(
self
.
version
)
.
split
(
'-'
)[
-
1
]
return
str
(
self
.
id
)
.
split
(
'-'
)[
-
1
]
def
run
(
self
):
def
get_latest_history
(
self
):
pass
return
self
.
history
.
all
()
.
order_by
(
'date_start'
)
.
last
()
def
__str__
(
self
):
def
__str__
(
self
):
return
"{} of {}"
.
format
(
self
.
subject
.
name
,
self
.
short_version
)
return
"{} of {}"
.
format
(
self
.
task
.
name
,
self
.
short_id
)
class
Meta
:
class
Meta
:
db_table
=
"ops_adhoc
_data
"
db_table
=
"ops_adhoc"
class
AdHocRunHistory
(
models
.
Model
):
class
AdHocRunHistory
(
models
.
Model
):
uuid
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
"""
adhoc
=
models
.
ForeignKey
(
AdHocData
,
on_delete
=
models
.
CASCADE
)
AdHoc running history.
"""
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
adhoc
=
models
.
ForeignKey
(
AdHoc
,
related_name
=
'history'
,
on_delete
=
models
.
CASCADE
)
date_start
=
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
_
(
'Start time'
))
date_start
=
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
_
(
'Start time'
))
date_finished
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'End time'
))
date_finished
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'End time'
))
timedelta
=
models
.
FloatField
(
default
=
0.0
,
verbose_name
=
_
(
'Time'
),
null
=
True
)
timedelta
=
models
.
FloatField
(
default
=
0.0
,
verbose_name
=
_
(
'Time'
),
null
=
True
)
is_finished
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Is finished'
))
is_finished
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Is finished'
))
is_success
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Is success'
))
is_success
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Is success'
))
result
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Playbook
raw result'
))
_result
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Adhoc
raw result'
))
summary
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Playbook
summary'
))
_summary
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Adhoc result
summary'
))
@property
@property
def
short_id
(
self
):
def
short_id
(
self
):
return
str
(
self
.
id
)
.
split
(
'-'
)[
-
1
]
return
str
(
self
.
id
)
.
split
(
'-'
)[
-
1
]
@property
def
result
(
self
):
return
json
.
loads
(
self
.
_result
)
@result.setter
def
result
(
self
,
item
):
self
.
_result
=
json
.
dumps
(
item
)
@property
def
summary
(
self
):
return
json
.
loads
(
self
.
_summary
)
@summary.setter
def
summary
(
self
,
item
):
self
.
_summary
=
json
.
dumps
(
item
)
def
__str__
(
self
):
def
__str__
(
self
):
return
self
.
short_id
return
self
.
short_id
...
...
apps/ops/serializers.py
View file @
0c9e24dc
...
@@ -2,12 +2,43 @@
...
@@ -2,12 +2,43 @@
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
.models
import
AdHoc
from
.models
import
Task
,
AdHoc
,
AdHocRunHistory
class
TaskSerializer
(
serializers
.
ModelSerializer
):
class
TaskSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
class
Meta
:
model
=
AdHoc
model
=
Task
fields
=
'__all__'
fields
=
'__all__'
class
AdHocSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
AdHoc
exclude
=
(
'_tasks'
,
'_options'
,
'_hosts'
,
'_become'
)
def
get_field_names
(
self
,
declared_fields
,
info
):
fields
=
super
()
.
get_field_names
(
declared_fields
,
info
)
fields
.
extend
([
'tasks'
,
'options'
,
'hosts'
,
'become'
,
'short_id'
])
return
fields
class
AdHocRunHistorySerializer
(
serializers
.
ModelSerializer
):
task
=
serializers
.
SerializerMethodField
()
adhoc_short_id
=
serializers
.
SerializerMethodField
()
class
Meta
:
model
=
AdHocRunHistory
exclude
=
(
'_result'
,
'_summary'
)
@staticmethod
def
get_adhoc_short_id
(
obj
):
return
obj
.
adhoc
.
short_id
@staticmethod
def
get_task
(
obj
):
return
obj
.
adhoc
.
task
.
id
def
get_field_names
(
self
,
declared_fields
,
info
):
fields
=
super
()
.
get_field_names
(
declared_fields
,
info
)
fields
.
extend
([
'summary'
,
'short_id'
])
return
fields
apps/ops/tasks.py
View file @
0c9e24dc
# coding: utf-8
# coding: utf-8
from
__future__
import
absolute_import
,
unicode_literals
from
celery
import
shared_task
from
celery
import
shared_task
from
common.utils
import
get_logger
from
.utils
import
run_adhoc
from
.utils
import
run_AdHoc
logger
=
get_logger
(
__file__
)
def
rerun_task
():
pass
@shared_task
@shared_task
def
rerun_task
(
task_id
):
def
run_add_hoc_and_record_async
(
adhoc
,
**
options
):
from
.models
import
Playbook
return
run_adhoc
(
adhoc
,
**
options
)
record
=
Playbook
.
objects
.
get
(
uuid
=
task_id
)
assets
=
record
.
assets_json
task_tuple
=
record
.
module_args
pattern
=
record
.
pattern
task_name
=
record
.
name
return
run_AdHoc
(
task_tuple
,
assets
,
pattern
=
pattern
,
task_name
=
task_name
,
task_id
=
task_id
)
apps/ops/templates/ops/task_adhoc.html
0 → 100644
View file @
0c9e24dc
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link
href=
"{% static "
css
/
plugins
/
select2
/
select2
.
min
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
plugins
/
sweetalert
/
sweetalert
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
plugins
/
select2
/
select2
.
full
.
min
.
js
"
%}"
></script>
<script
src=
"{% static "
js
/
plugins
/
sweetalert
/
sweetalert
.
min
.
js
"
%}"
></script>
{% endblock %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"panel-options"
>
<ul
class=
"nav nav-tabs"
>
<li>
<a
href=
"{% url 'ops:task-detail' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task detail' %}
</a>
</li>
<li
class=
"active"
>
<a
href=
"{% url 'ops:task-adhoc' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task versions' %}
</a>
</li>
<li>
<a
href=
"{% url 'ops:task-history' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Run history' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
<div
class=
"col-sm-12"
style=
"padding-left: 0"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<span
style=
"float: left"
>
{% trans 'Versions of ' %}
<b>
{{ object.name }}
</b></span>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<ul
class=
"dropdown-menu dropdown-user"
>
</ul>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<table
class=
"table table-hover "
id=
"task-version-list-table"
>
<thead>
<tr>
<th
class=
"text-center"
>
<input
type=
"checkbox"
id=
"check_all"
class=
"ipt_check_all"
>
</th>
<th>
{% trans 'Version' %}
</th>
<th>
{% trans 'Hosts' %}
</th>
<th>
{% trans 'Pattern' %}
</th>
<th>
{% trans 'Run as' %}
</th>
<th>
{% trans 'Become' %}
</th>
<th>
{% trans 'Datetime' %}
</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
var
options
=
{
ele
:
$
(
'#task-version-list-table'
),
buttons
:
[],
order
:
[],
select
:
[],
columnDefs
:
[
{
targets
:
1
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
{
#
var
detail_btn
=
'<a href="'
+
cellData
+
'</a>'
;
#
}
$
(
td
).
html
(
cellData
);
}},
{
targets
:
2
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
var
dataLength
=
cellData
.
length
;
$
(
td
).
html
(
dataLength
);
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
!
cellData
)
{
$
(
td
).
html
(
"Admin"
)
}
else
{
$
(
td
).
html
(
cellData
)
}
}},
{
targets
:
5
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
if
(
!
cellData
)
{
$
(
td
).
html
(
""
)
}
else
{
$
(
td
).
html
(
cellData
.
user
)
}
}}
],
ajax_url
:
'{% url "api-ops:adhoc-list" %}?task={{ object.pk }}'
,
columns
:
[{
data
:
function
(){
return
""
}},
{
data
:
"short_id"
},
{
data
:
"hosts"
},
{
data
:
"pattern"
},
{
data
:
"run_as"
},
{
data
:
"become"
},
{
data
:
"date_created"
}]
};
jumpserver
.
initDataTable
(
options
);
})
</script>
{% endblock %}
apps/ops/templates/ops/task_detail.html
View file @
0c9e24dc
...
@@ -15,8 +15,14 @@
...
@@ -15,8 +15,14 @@
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"panel-options"
>
<div
class=
"panel-options"
>
<ul
class=
"nav nav-tabs"
>
<ul
class=
"nav nav-tabs"
>
<li
class=
"active"
>
<li
class=
"active"
>
<a
href=
""
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task replay detail' %}
</a>
<a
href=
"{% url 'ops:task-detail' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task detail' %}
</a>
</li>
<li>
<a
href=
"{% url 'ops:task-adhoc' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task versions' %}
</a>
</li>
<li>
<a
href=
"{% url 'ops:task-history' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Run history' %}
</a>
</li>
</li>
</ul>
</ul>
</div>
</div>
...
@@ -43,49 +49,47 @@
...
@@ -43,49 +49,47 @@
<table
class=
"table"
>
<table
class=
"table"
>
<tbody>
<tbody>
<tr
class=
"no-borders-tr"
>
<tr
class=
"no-borders-tr"
>
<td
width=
"20%"
>
{% trans '
UU
ID' %}:
</td>
<td
width=
"20%"
>
{% trans 'ID' %}:
</td>
<td><b>
{{ object.
uu
id }}
</b></td>
<td><b>
{{ object.id }}
</b></td>
</tr>
</tr>
<tr>
<tr>
<td
width=
"20%"
>
{% trans 'Name' %}:
</td>
<td
width=
"20%"
>
{% trans 'Name' %}:
</td>
<td><b>
{{ object.name }}
</b></td>
<td><b>
{{ object.name }}
</b></td>
</tr>
</tr>
<tr>
<tr>
<td>
{% trans 'Date start' %}:
</td>
<td>
{% trans 'Date created' %}:
</td>
<td><b>
{{ object.date_start }}
</b></td>
<td><b>
{{ object.date_created }}
</b></td>
</tr>
<tr>
<td>
{% trans 'Total versions' %}
</td>
<td><b>
{{ object.adhoc.all |length }}
</b></td>
</tr>
</tr>
<tr>
<tr>
<td>
{% trans 'Date finished' %}:
</td>
<td>
{% trans 'Last version' %}
</td>
<td><b>
{{ object.date_finished }}
</b></td>
<td><b>
{{ object.get_latest_adhoc.short_id }}
</b></td>
</tr>
<tr>
<td>
{% trans 'Latest run' %}:
</td>
<td><b>
{{ object.get_latest_history.date_start }}
</b></td>
</tr>
</tr>
<tr>
<tr>
<td>
{% trans 'Time delta' %}:
</td>
<td>
{% trans 'Time delta' %}:
</td>
<td><b>
{{ object.
timedelta
}} s
</b></td>
<td><b>
{{ object.
get_latest_history.timedelta|floatformat
}} s
</b></td>
</tr>
</tr>
<tr>
<tr>
<td>
{% trans 'Is finished' %}:
</td>
<td>
{% trans 'Is finished' %}:
</td>
<td><b>
{{ object.is_finished|yesno:"Yes,No,Unkown" }}
</b></td>
<td><b>
{{ object.
get_latest_history.
is_finished|yesno:"Yes,No,Unkown" }}
</b></td>
</tr>
</tr>
<tr>
<tr>
<td>
{% trans 'Is success ' %}:
</td>
<td>
{% trans 'Is success ' %}:
</td>
{% if object.is_finished %}
<td><b>
{{ object.get_latest_history.is_success|yesno:"Yes,No,Unkown" }}
</b></td>
<td><b>
{{ object.is_success|yesno:"Yes,No,Unkown" }}
</b></td>
{% else %}
<td>
<div
class=
"progress progress-striped active"
>
<div
style=
"width: 50%"
aria-valuemax=
"100"
aria-valuemin=
"0"
aria-valuenow=
"75"
role=
"progressbar"
class=
"progress-bar progress-bar-primary"
>
<span
class=
"sr-only"
>
40% Complete (success)
</span>
</div>
</div>
</td>
{% endif %}
</tr>
</tr>
<tr>
<tr>
<td>
{% trans '
asse
ts' %}:
</td>
<td>
{% trans '
Conen
ts' %}:
</td>
<td>
<td>
<b>
<b>
{% for
asset in object.total_asset
s %}
{% for
task in object.get_latest_adhoc.task
s %}
{{
asset.hostnam
e }}
<br/>
{{
task.name }} : {{ task.action.modul
e }}
<br/>
{% endfor %}
{% endfor %}
</b>
</b>
</td>
</td>
...
@@ -94,31 +98,6 @@
...
@@ -94,31 +98,6 @@
</table>
</table>
</div>
</div>
</div>
</div>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<span><b>
Result
</b></span>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<ul
class=
"dropdown-menu dropdown-user"
>
</ul>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<pre>
{{ object.result }}
</pre>
</div>
</div>
</div>
</div>
</div>
<div
class=
"col-sm-5"
style=
"padding-left: 0;padding-right: 0"
>
<div
class=
"col-sm-5"
style=
"padding-left: 0;padding-right: 0"
>
<div
class=
"panel panel-danger"
>
<div
class=
"panel panel-danger"
>
...
@@ -154,7 +133,7 @@
...
@@ -154,7 +133,7 @@
<div
class=
"panel-body"
>
<div
class=
"panel-body"
>
<table
class=
"table"
>
<table
class=
"table"
>
<tbody>
<tbody>
{% for host in
results.success
%}
{% for host in
object.get_latest_history.summary.contacted
%}
{% if forloop.first %}
{% if forloop.first %}
<tr
class=
"no-borders-tr"
>
<tr
class=
"no-borders-tr"
>
{% else %}
{% else %}
...
...
apps/ops/templates/ops/task_history.html
0 → 100644
View file @
0c9e24dc
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link
href=
"{% static "
css
/
plugins
/
select2
/
select2
.
min
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
plugins
/
sweetalert
/
sweetalert
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
plugins
/
select2
/
select2
.
full
.
min
.
js
"
%}"
></script>
<script
src=
"{% static "
js
/
plugins
/
sweetalert
/
sweetalert
.
min
.
js
"
%}"
></script>
{% endblock %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"panel-options"
>
<ul
class=
"nav nav-tabs"
>
<li>
<a
href=
"{% url 'ops:task-detail' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task detail' %}
</a>
</li>
<li>
<a
href=
"{% url 'ops:task-adhoc' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Task versions' %}
</a>
</li>
<li
class=
"active"
>
<a
href=
"{% url 'ops:task-history' pk=object.pk %}"
class=
"text-center"
><i
class=
"fa fa-laptop"
></i>
{% trans 'Run history' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
<div
class=
"col-sm-12"
style=
"padding-left: 0"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<span
style=
"float: left"
>
{% trans 'Versions of ' %}
<b>
{{ object.name }}
</b></span>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<ul
class=
"dropdown-menu dropdown-user"
>
</ul>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<table
class=
"table table-hover "
id=
"task-history-list-table"
>
<thead>
<tr>
<th
class=
"text-center"
>
<input
type=
"checkbox"
id=
"check_all"
class=
"ipt_check_all"
>
</th>
<th>
{% trans 'Date start' %}
</th>
<th>
{% trans 'F/S/T' %}
</th>
<th>
{% trans 'Is finished' %}
</th>
<th>
{% trans 'Is success' %}
</th>
<th>
{% trans 'Time' %}
</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
var
options
=
{
ele
:
$
(
'#task-history-list-table'
),
buttons
:
[],
order
:
[],
select
:
[],
columnDefs
:
[
{
targets
:
1
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
{
#
var
detail_btn
=
'<a href="'
+
cellData
+
'</a>'
;
#
}
$
(
td
).
html
(
cellData
);
}},
{
#
{
targets
:
2
,
createdCell
:
function
(
td
,
cellData
,
rowData
)
{
#
}
{
#
var
dataLength
=
cellData
.
length
;
#
}
{
#
$
(
td
).
html
(
dataLength
);
#
}
{
#
}},
#
}
{
targets
:
3
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
!
cellData
)
{
$
(
td
).
html
(
'<i class="fa fa-times text-danger"></i>'
)
}
else
{
$
(
td
).
html
(
'<i class="fa fa-check text-navy"></i>'
)
}
}},
{
targets
:
4
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
!
cellData
)
{
$
(
td
).
html
(
'<i class="fa fa-times text-danger"></i>'
)
}
else
{
$
(
td
).
html
(
'<i class="fa fa-check text-navy"></i>'
)
}
}},
{
targets
:
5
,
createdCell
:
function
(
td
,
cellData
)
{
if
(
cellData
)
{
$
(
td
).
html
(
cellData
.
toFixed
(
2
)
+
' s'
)
}
else
{
$
(
td
).
html
(
"0"
+
' s'
)
}
}}
],
ajax_url
:
'{% url "api-ops:history-list" %}?task={{ object.pk }}'
,
columns
:
[{
data
:
function
(){
return
""
}},
{
data
:
"date_start"
},
{
data
:
"adhoc_short_id"
},
{
data
:
"adhoc_short_id"
},
{
data
:
"adhoc_short_id"
},
{
data
:
"timedelta"
}]
};
jumpserver
.
initDataTable
(
options
);
})
</script>
{% endblock %}
apps/ops/templates/ops/task_list.html
View file @
0c9e24dc
...
@@ -22,7 +22,7 @@
...
@@ -22,7 +22,7 @@
</div>
</div>
</div>
</div>
<div
class=
"input-group"
>
<div
class=
"input-group"
>
<input
type=
"text"
class=
"form-control input-sm"
name=
"keyword"
placeholder=
"
Keyword
"
value=
"{{ keyword }}"
>
<input
type=
"text"
class=
"form-control input-sm"
name=
"keyword"
placeholder=
"
Search
"
value=
"{{ keyword }}"
>
</div>
</div>
<div
class=
"input-group"
>
<div
class=
"input-group"
>
<div
class=
"input-group-btn"
>
<div
class=
"input-group-btn"
>
...
@@ -37,10 +37,11 @@
...
@@ -37,10 +37,11 @@
{% block table_head %}
{% block table_head %}
<th
class=
"text-center"
></th>
<th
class=
"text-center"
></th>
<th
class=
"text-center"
>
{% trans 'Name' %}
</th>
<th
class=
"text-center"
>
{% trans 'Name' %}
</th>
<th
class=
"text-center"
>
{% trans 'Asset' %}
</th>
<th
class=
"text-center"
>
{% trans 'F/S/T' %}
</th>
<th
class=
"text-center"
>
{% trans 'Versions' %}
</th>
<th
class=
"text-center"
>
{% trans 'Hosts' %}
</th>
<th
class=
"text-center"
>
{% trans 'Success' %}
</th>
<th
class=
"text-center"
>
{% trans 'Success' %}
</th>
<th
class=
"text-center"
>
{% trans 'Finished' %}
</th>
<th
class=
"text-center"
>
{% trans 'Date' %}
</th>
<th
class=
"text-center"
>
{% trans 'Date start' %}
</th>
<th
class=
"text-center"
>
{% trans 'Time' %}
</th>
<th
class=
"text-center"
>
{% trans 'Time' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
<th
class=
"text-center"
>
{% trans 'Action' %}
</th>
{% endblock %}
{% endblock %}
...
@@ -49,26 +50,23 @@
...
@@ -49,26 +50,23 @@
{% for object in task_list %}
{% for object in task_list %}
<tr
class=
"gradeX"
>
<tr
class=
"gradeX"
>
<td
class=
"text-center"
><input
type=
"checkbox"
class=
"cbx-term"
>
</td>
<td
class=
"text-center"
><input
type=
"checkbox"
class=
"cbx-term"
>
</td>
<td
class=
"text-center"
><a
href=
"{% url 'ops:task-detail' pk=object.uuid %}"
>
{{ object.name }}
</a></td>
<td
class=
"text-center"
><a
href=
"{% url 'ops:task-detail' pk=object.id %}"
>
{{ object.name }}
</a></td>
<td
class=
"text-center"
>
{{ object.total_assets|length }}
</td>
<td
class=
"text-center"
>
<td
class=
"text-center"
>
{% if object.is_success %}
<span
class=
"text-danger"
>
{{ object.get_all_run_times.failed }}
</span>
/
<span
class=
"text-navy"
>
{{ object.get_all_run_times.success}}
</span>
/{{ object.get_all_run_times.total}}
<i
class=
"fa fa-check text-navy"
></i>
{% else %}
<i
class=
"fa fa-times text-danger"
></i>
{% endif %}
</td>
</td>
<td
class=
"text-center"
>
{{ object.adhoc.all | length}}
</td>
<td
class=
"text-center"
>
{{ object.get_latest_adhoc.hosts | length}}
</td>
<td
class=
"text-center"
>
<td
class=
"text-center"
>
{% if object.
is_finished
%}
{% if object.
get_latest_history.is_success
%}
<i
class=
"fa fa-check text-navy"
></i>
<i
class=
"fa fa-check text-navy"
></i>
{% else %}
{% else %}
<i
class=
"fa fa-times text-danger"
></i>
<i
class=
"fa fa-times text-danger"
></i>
{% endif %}
{% endif %}
</td>
</td>
<td
class=
"text-center"
>
{{ object.date_start }}
</td>
<td
class=
"text-center"
>
{{ object.
get_latest_history.
date_start }}
</td>
<td
class=
"text-center"
>
{{ object.
timedelta
}} s
</td>
<td
class=
"text-center"
>
{{ object.
get_latest_history.timedelta|floatformat
}} s
</td>
<td
class=
"text-center"
>
<td
class=
"text-center"
>
<a
href=
"{% url 'ops:task-run' pk=object.
uuid %}"
class=
"btn btn-xs btn-info"
>
{% trans "Run agai
n" %}
</a>
<a
href=
"{% url 'ops:task-run' pk=object.
id %}"
class=
"btn btn-xs btn-info"
>
{% trans "Ru
n" %}
</a>
<a
data-uid=
"{{ object.uuid }}"
class=
"btn btn-xs btn-danger btn-del"
>
{% trans "Delete" %}
</a>
<a
data-uid=
"{{ object.uuid }}"
class=
"btn btn-xs btn-danger btn-del"
>
{% trans "Delete" %}
</a>
</td>
</td>
</tr>
</tr>
...
...
apps/ops/test_utils.py
View file @
0c9e24dc
...
@@ -5,17 +5,16 @@ import sys
...
@@ -5,17 +5,16 @@ import sys
import
os
import
os
from
django.test
import
TestCase
from
django.test
import
TestCase
os
.
environ
.
setdefault
(
"DJANGO_SETTINGS_MODULE"
,
"jumpserver.settings"
)
from
ops.models
import
Task
,
AdHoc
from
ops.models
import
AdHoc
,
AdHocData
from
ops.utils
import
run_adhoc_object
from
ops.utils
import
run_adhoc
class
TestRunAdHoc
(
TestCase
):
class
TestRunAdHoc
(
TestCase
):
def
setUp
(
self
):
def
setUp
(
self
):
adhoc
=
AdHoc
(
name
=
"Test run adhoc"
)
adhoc
=
Task
(
name
=
"Test run adhoc"
)
adhoc
.
save
()
adhoc
.
save
()
self
.
data
=
AdHoc
Data
(
subject
=
adhoc
,
run_as_admin
=
True
,
pattern
=
'all'
)
self
.
data
=
AdHoc
(
subject
=
adhoc
,
run_as_admin
=
True
,
pattern
=
'all'
)
self
.
data
.
tasks
=
[
self
.
data
.
tasks
=
[
{
'name'
:
'run ls'
,
'action'
:
{
'module'
:
'shell'
,
'args'
:
'ls'
}},
{
'name'
:
'run ls'
,
'action'
:
{
'module'
:
'shell'
,
'args'
:
'ls'
}},
{
'name'
:
'echo '
,
'action'
:
{
'module'
:
'shell'
,
'args'
:
'echo 123'
}},
{
'name'
:
'echo '
,
'action'
:
{
'module'
:
'shell'
,
'args'
:
'echo 123'
}},
...
...
apps/ops/urls/api_urls.py
View file @
0c9e24dc
...
@@ -7,6 +7,8 @@ from .. import api
...
@@ -7,6 +7,8 @@ from .. import api
router
=
DefaultRouter
()
router
=
DefaultRouter
()
router
.
register
(
r'v1/tasks'
,
api
.
TaskViewSet
,
'task'
)
router
.
register
(
r'v1/tasks'
,
api
.
TaskViewSet
,
'task'
)
router
.
register
(
r'v1/adhoc'
,
api
.
AdHocViewSet
,
'adhoc'
)
router
.
register
(
r'v1/history'
,
api
.
AdHocRunHistorySet
,
'history'
)
urlpatterns
=
[]
urlpatterns
=
[]
...
...
apps/ops/urls/view_urls.py
View file @
0c9e24dc
...
@@ -11,5 +11,7 @@ urlpatterns = [
...
@@ -11,5 +11,7 @@ urlpatterns = [
# TResource Task url
# TResource Task url
url
(
r'^task/$'
,
views
.
TaskListView
.
as_view
(),
name
=
'task-list'
),
url
(
r'^task/$'
,
views
.
TaskListView
.
as_view
(),
name
=
'task-list'
),
url
(
r'^task/(?P<pk>[0-9a-zA-Z\-]+)/$'
,
views
.
TaskDetailView
.
as_view
(),
name
=
'task-detail'
),
url
(
r'^task/(?P<pk>[0-9a-zA-Z\-]+)/$'
,
views
.
TaskDetailView
.
as_view
(),
name
=
'task-detail'
),
url
(
r'^task/(?P<pk>[0-9a-zA-Z\-]+)/adhoc/$'
,
views
.
TaskAdhocView
.
as_view
(),
name
=
'task-adhoc'
),
url
(
r'^task/(?P<pk>[0-9a-zA-Z\-]+)/history/$'
,
views
.
TaskHistoryView
.
as_view
(),
name
=
'task-history'
),
url
(
r'^task/(?P<pk>[0-9a-zA-Z\-]+)/run/$'
,
views
.
TaskRunView
.
as_view
(),
name
=
'task-run'
),
url
(
r'^task/(?P<pk>[0-9a-zA-Z\-]+)/run/$'
,
views
.
TaskRunView
.
as_view
(),
name
=
'task-run'
),
]
]
\ No newline at end of file
apps/ops/utils.py
View file @
0c9e24dc
...
@@ -4,20 +4,16 @@ import re
...
@@ -4,20 +4,16 @@ import re
import
time
import
time
from
django.utils
import
timezone
from
django.utils
import
timezone
from
common.utils
import
get_logger
,
get_object_or_none
from
common.utils
import
get_logger
,
get_object_or_none
,
get_short_uuid_str
from
.ansible
import
AdHocRunner
from
.ansible
import
AdHocRunner
,
CommandResultCallback
from
.inventory
import
JMSInventory
from
.ansible.exceptions
import
AnsibleError
from
.ansible.exceptions
import
AnsibleError
from
.models
import
AdHocRunHistory
from
.models
import
AdHocRunHistory
,
Task
,
AdHoc
from
assets.utils
import
get_assets_by_hostname_list
logger
=
get_logger
(
__file__
)
logger
=
get_logger
(
__file__
)
UUID_PATTERN
=
re
.
compile
(
r'[0-9a-zA-Z\-]{36}'
)
UUID_PATTERN
=
re
.
compile
(
r'[0-9a-zA-Z\-]{36}'
)
def
run_AdHoc
():
pass
def
is_uuid
(
s
):
def
is_uuid
(
s
):
if
UUID_PATTERN
.
match
(
s
):
if
UUID_PATTERN
.
match
(
s
):
return
True
return
True
...
@@ -25,97 +21,133 @@ def is_uuid(s):
...
@@ -25,97 +21,133 @@ def is_uuid(s):
return
False
return
False
def
asset_to_dict
(
asset
):
def
record_adhoc
(
func
):
return
asset
.
to_json
()
def
_deco
(
adhoc
,
**
options
):
record
=
AdHocRunHistory
(
adhoc
=
adhoc
)
time_start
=
time
.
time
()
def
asset_to_dict_with_credential
(
asset
):
try
:
return
asset
.
_to_secret_json
()
result
=
func
(
adhoc
,
**
options
)
record
.
is_finished
=
True
if
result
.
results_summary
.
get
(
'dark'
):
def
system_user_to_dict_with_credential
(
system_user
):
record
.
is_success
=
False
return
system_user
.
_to_secret_json
()
else
:
record
.
is_success
=
True
record
.
result
=
result
.
results_raw
def
get_hosts_with_admin
(
hostname_list
):
record
.
summary
=
result
.
results_summary
assets
=
get_assets_by_hostname_list
(
hostname_list
)
return
result
return
[
asset
.
_to_secret_json
for
asset
in
assets
]
finally
:
record
.
date_finished
=
timezone
.
now
()
record
.
timedelta
=
time
.
time
()
-
time_start
def
get_hosts
(
hostname_list
):
record
.
save
()
assets
=
get_assets_by_hostname_list
(
hostname_list
)
return
_deco
return
[
asset
.
to_json
for
asset
in
assets
]
def
get_adhoc_inventory
(
adhoc
):
def
get_run_user
(
name
):
if
adhoc
.
become
:
from
assets.models
import
SystemUser
become_info
=
{
system_user
=
get_object_or_none
(
SystemUser
,
name
=
name
)
'become'
:
{
if
system_user
is
None
:
adhoc
.
become
return
{}
}
}
else
:
else
:
return
system_user
.
_to_secret_json
()
become_info
=
None
inventory
=
JMSInventory
(
adhoc
.
hosts
,
run_as_admin
=
adhoc
.
run_as_admin
,
run_as
=
adhoc
.
run_as
,
become_info
=
become_info
)
return
inventory
def
get_hosts_with_run_user
(
hostname_list
,
run_as
):
hosts_dict
=
get_hosts
(
hostname_list
)
system_user_dct
=
get_run_user
(
run_as
)
for
host
in
hosts_dict
:
def
get_inventory
(
hostname_list
,
run_as_admin
=
False
,
run_as
=
None
,
become_info
=
None
):
host
.
update
(
system_user_dct
)
return
JMSInventory
(
return
hosts_dict
hostname_list
,
run_as_admin
=
run_as_admin
,
run_as
=
run_as
,
become_info
=
become_info
)
def
hosts_add_become
(
hosts
,
adhoc_data
):
def
get_adhoc_runner
(
hostname_list
,
run_as_admin
=
False
,
run_as
=
None
,
become_info
=
None
):
if
adhoc_data
.
become
:
inventory
=
get_inventory
(
become_data
=
{
hostname_list
,
run_as_admin
=
run_as_admin
,
"become"
:
{
run_as
=
run_as
,
become_info
=
become_info
"method"
:
adhoc_data
.
become_method
,
)
"user"
:
adhoc_data
.
become_user
,
runner
=
AdHocRunner
(
inventory
)
"pass"
:
adhoc_data
.
become_pass
,
return
runner
}
}
for
host
in
hosts
:
host
.
update
(
become_data
)
return
hosts
def
run_adhoc
(
adhoc_data
,
**
options
):
@record_adhoc
def
run_adhoc_object
(
adhoc
,
**
options
):
"""
"""
:param adhoc
_data: Instance of AdHocData
:param adhoc
: Instance of AdHoc
:param options: ansible support option, like forks ...
:param options: ansible support option, like forks ...
:return:
:return:
"""
"""
name
=
adhoc_data
.
subject
.
name
name
=
adhoc
.
task
.
name
hostname_list
=
adhoc_data
.
hosts
inventory
=
get_adhoc_inventory
(
adhoc
)
if
adhoc_data
.
run_as_admin
:
runner
=
AdHocRunner
(
inventory
)
hosts
=
get_hosts_with_admin
(
hostname_list
)
else
:
hosts
=
get_hosts_with_run_user
(
hostname_list
,
adhoc_data
.
run_as
)
hosts_add_become
(
hosts
,
adhoc_data
)
# admin user 自带become
runner
=
AdHocRunner
(
hosts
)
for
k
,
v
in
options
:
for
k
,
v
in
options
:
runner
.
set_option
(
k
,
v
)
runner
.
set_option
(
k
,
v
)
record
=
AdHocRunHistory
(
adhoc
=
adhoc_data
)
time_start
=
time
.
time
()
try
:
try
:
result
=
runner
.
run
(
adhoc_data
.
tasks
,
adhoc_data
.
pattern
,
name
)
result
=
runner
.
run
(
adhoc
.
tasks
,
adhoc
.
pattern
,
name
)
record
.
is_finished
=
True
if
result
.
results_summary
.
get
(
'dark'
):
record
.
is_success
=
False
else
:
record
.
is_success
=
True
record
.
result
=
result
.
results_raw
record
.
summary
=
result
.
results_summary
return
result
return
result
except
AnsibleError
as
e
:
except
AnsibleError
as
e
:
logger
.
error
(
"Failed run adhoc {}, {}"
.
format
(
name
,
e
))
logger
.
error
(
"Failed run adhoc {}, {}"
.
format
(
name
,
e
))
raise
raise
finally
:
record
.
date_finished
=
timezone
.
now
()
record
.
timedelta
=
time
.
time
()
-
time_start
record
.
save
()
def
run_adhoc
(
hostname_list
,
pattern
,
tasks
,
name
=
None
,
run_as_admin
=
False
,
run_as
=
None
,
become_info
=
None
):
if
name
is
None
:
name
=
"Adhoc-task-{}-{}"
.
format
(
get_short_uuid_str
(),
timezone
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
),
)
inventory
=
get_inventory
(
hostname_list
,
run_as_admin
=
run_as_admin
,
run_as
=
run_as
,
become_info
=
become_info
)
runner
=
AdHocRunner
(
inventory
)
return
runner
.
run
(
tasks
,
pattern
,
play_name
=
name
)
def
create_and_run_adhoc
(
hostname_list
,
pattern
,
tasks
,
name
=
None
,
run_as_admin
=
False
,
run_as
=
None
,
become_info
=
None
):
if
name
is
None
:
name
=
"Adhoc-task-{}-{}"
.
format
(
get_short_uuid_str
(),
timezone
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
d
%
H:
%
M:
%
S"
),
)
task
=
Task
(
name
=
name
)
task
.
save
()
adhoc
=
AdHoc
(
task
=
task
,
pattern
=
pattern
,
name
=
name
,
run_as_admin
=
run_as_admin
,
run_as
=
run_as
)
adhoc
.
hosts
=
hostname_list
adhoc
.
tasks
=
tasks
adhoc
.
become
=
become_info
adhoc
.
save
()
def
get_task_by_name
(
name
):
task
=
get_object_or_none
(
Task
,
name
=
name
)
return
task
def
create_task
(
name
,
created_by
=
""
):
return
Task
.
objects
.
create
(
name
=
name
,
created_by
=
created_by
)
def
create_adhoc
(
task
,
hosts
,
tasks
,
pattern
=
'all'
,
options
=
None
,
run_as_admin
=
False
,
run_as
=
""
,
become_info
=
None
,
created_by
=
""
):
adhoc
=
AdHoc
(
task
=
task
,
pattern
=
pattern
,
run_as_admin
=
run_as_admin
,
run_as
=
run_as
,
created_by
=
created_by
)
adhoc
.
hosts
=
hosts
adhoc
.
tasks
=
tasks
adhoc
.
options
=
options
adhoc
.
become
=
become_info
adhoc
.
save
()
return
adhoc
apps/ops/views.py
View file @
0c9e24dc
...
@@ -9,40 +9,40 @@ from django.views.generic import ListView, DetailView, View
...
@@ -9,40 +9,40 @@ from django.views.generic import ListView, DetailView, View
from
django.utils
import
timezone
from
django.utils
import
timezone
from
django.shortcuts
import
redirect
,
reverse
from
django.shortcuts
import
redirect
,
reverse
from
.models
import
AdHoc
,
AdHocData
,
AdHocRunHistory
from
.models
import
Task
,
AdHoc
,
AdHocRunHistory
from
ops.tasks
import
rerun_task
from
ops.tasks
import
rerun_task
class
TaskListView
(
ListView
):
class
TaskListView
(
ListView
):
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
model
=
AdHoc
model
=
Task
ordering
=
(
'-date_
start
'
,)
ordering
=
(
'-date_
created
'
,)
context_object_name
=
'task_list'
context_object_name
=
'task_list'
template_name
=
'ops/task_list.html'
template_name
=
'ops/task_list.html'
date_format
=
'
%
m/
%
d/
%
Y'
date_format
=
'
%
m/
%
d/
%
Y'
keyword
=
date_from_s
=
date_to_s
=
''
keyword
=
date_from_s
=
date_to_s
=
''
def
get_queryset
(
self
):
def
get_queryset
(
self
):
date_
now
=
timezone
.
localtime
(
timezone
.
now
()
)
date_
to_default
=
timezone
.
now
(
)
date_
to_default
=
date_now
.
strftime
(
self
.
date_format
)
date_
from_default
=
timezone
.
now
()
-
timezone
.
timedelta
(
7
)
date_from_default
=
(
date_now
-
timezone
.
timedelta
(
7
))
\
date_from_default
_s
=
date_from_default
.
strftime
(
self
.
date_format
)
.
strftime
(
self
.
date_format
)
date_to_default_s
=
date_to_default
.
strftime
(
self
.
date_format
)
self
.
queryset
=
super
(
TaskListView
,
self
)
.
get_queryset
()
self
.
queryset
=
super
()
.
get_queryset
()
self
.
keyword
=
self
.
request
.
GET
.
get
(
'keyword'
,
''
)
self
.
keyword
=
self
.
request
.
GET
.
get
(
'keyword'
,
''
)
self
.
date_from_s
=
self
.
request
.
GET
.
get
(
'date_from'
,
date_from_default
)
self
.
date_from_s
=
self
.
request
.
GET
.
get
(
'date_from'
,
date_from_default
_s
)
self
.
date_to_s
=
self
.
request
.
GET
.
get
(
'date_to'
,
date_to_default
)
self
.
date_to_s
=
self
.
request
.
GET
.
get
(
'date_to'
,
date_to_default
_s
)
if
self
.
date_from_s
:
if
self
.
date_from_s
:
date_from
=
datetime
.
strptime
(
self
.
date_from_s
,
self
.
date_format
)
date_from
=
datetime
.
strptime
(
self
.
date_from_s
,
self
.
date_format
)
date_from
=
date_from
.
replace
(
tzinfo
=
timezone
.
get_current_timezone
())
date_from
=
date_from
.
replace
(
tzinfo
=
timezone
.
get_current_timezone
())
self
.
queryset
=
self
.
queryset
.
filter
(
date_
start
__gt
=
date_from
)
self
.
queryset
=
self
.
queryset
.
filter
(
date_
created
__gt
=
date_from
)
if
self
.
date_to_s
:
if
self
.
date_to_s
:
date_to
=
timezone
.
datetime
.
strptime
(
date_to
=
timezone
.
datetime
.
strptime
(
self
.
date_to_s
+
' 23:59:59'
,
'
%
m/
%
d/
%
Y
%
H:
%
M:
%
S'
)
self
.
date_to_s
+
' 23:59:59'
,
'
%
m/
%
d/
%
Y
%
H:
%
M:
%
S'
)
date_to
=
date_to
.
replace
(
tzinfo
=
timezone
.
get_current_timezone
())
date_to
=
date_to
.
replace
(
tzinfo
=
timezone
.
get_current_timezone
())
self
.
queryset
=
self
.
queryset
.
filter
(
date_
finish
ed__lt
=
date_to
)
self
.
queryset
=
self
.
queryset
.
filter
(
date_
creat
ed__lt
=
date_to
)
if
self
.
keyword
:
if
self
.
keyword
:
self
.
queryset
=
self
.
queryset
.
filter
(
self
.
queryset
=
self
.
queryset
.
filter
(
...
@@ -63,17 +63,42 @@ class TaskListView(ListView):
...
@@ -63,17 +63,42 @@ class TaskListView(ListView):
class
TaskDetailView
(
DetailView
):
class
TaskDetailView
(
DetailView
):
model
=
AdHocRunHistory
model
=
Task
template_name
=
'ops/task_detail.html'
template_name
=
'ops/task_detail.html'
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
context
=
{
'app'
:
'Ops'
,
'app'
:
'Ops'
,
'action'
:
'Playbook record detail'
,
'action'
:
'Task detail'
,
'results'
:
json
.
loads
(
self
.
object
.
summary
or
'{}'
),
}
}
kwargs
.
update
(
context
)
kwargs
.
update
(
context
)
return
super
(
TaskDetailView
,
self
)
.
get_context_data
(
**
kwargs
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
TaskAdhocView
(
DetailView
):
model
=
Task
template_name
=
'ops/task_adhoc.html'
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
'Ops'
,
'action'
:
'Task versions'
,
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
TaskHistoryView
(
DetailView
):
model
=
Task
template_name
=
'ops/task_history.html'
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
'Ops'
,
'action'
:
'Task run history'
,
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
TaskRunView
(
View
):
class
TaskRunView
(
View
):
...
...
apps/perms/tasks.py
View file @
0c9e24dc
...
@@ -3,46 +3,11 @@ from __future__ import absolute_import, unicode_literals
...
@@ -3,46 +3,11 @@ from __future__ import absolute_import, unicode_literals
from
celery
import
shared_task
from
celery
import
shared_task
from
common.utils
import
get_logger
,
encrypt_password
from
common.utils
import
get_logger
,
encrypt_password
from
ops.utils
import
run_AdHoc
logger
=
get_logger
(
__file__
)
logger
=
get_logger
(
__file__
)
@shared_task
(
bind
=
True
)
@shared_task
(
bind
=
True
)
def
push_users
(
self
,
assets
,
users
):
def
push_users
(
self
,
assets
,
users
):
"""
pass
user: {
name: 'web',
username: 'web',
shell: '/bin/bash',
password: '123123123',
public_key: 'string',
sudo: '/bin/whoami,/sbin/ifconfig'
}
"""
if
isinstance
(
users
,
dict
):
users
=
[
users
]
if
isinstance
(
assets
,
dict
):
assets
=
[
assets
]
task_tuple
=
[]
for
user
in
users
:
# 添加用户, 设置公钥, 设置sudo
task_tuple
.
extend
([
(
'user'
,
'name={} shell={} state=present password={}'
.
format
(
user
[
'username'
],
user
.
get
(
'shell'
,
'/bin/bash'
),
encrypt_password
(
user
.
get
(
'password'
,
None
)))),
(
'authorized_key'
,
"user={} state=present key='{}'"
.
format
(
user
[
'username'
],
user
[
'public_key'
])),
(
'lineinfile'
,
"dest=/etc/sudoers state=present regexp='^{0} ALL=' "
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
"validate='visudo -cf
%
s'"
.
format
(
user
[
'username'
],
user
.
get
(
'sudo'
,
'/sbin/ifconfig'
)
))
])
task_name
=
'Push user {}'
.
format
(
','
.
join
([
user
[
'name'
]
for
user
in
users
]))
task
=
run_AdHoc
(
task_tuple
,
assets
,
pattern
=
'all'
,
task_name
=
task_name
,
task_id
=
self
.
request
.
id
)
return
task
apps/templates/_nav.html
View file @
0c9e24dc
...
@@ -52,7 +52,7 @@
...
@@ -52,7 +52,7 @@
<i
class=
"fa fa-coffee"
></i>
<span
class=
"nav-label"
>
{% trans 'Job Center' %}
</span><span
class=
"fa arrow"
></span>
<i
class=
"fa fa-coffee"
></i>
<span
class=
"nav-label"
>
{% trans 'Job Center' %}
</span><span
class=
"fa arrow"
></span>
</a>
</a>
<ul
class=
"nav nav-second-level"
>
<ul
class=
"nav nav-second-level"
>
<li
id=
"task"
><a
href=
"{% url 'ops:task-list' %}"
>
{% trans '
Playbook
' %}
</a></li>
<li
id=
"task"
><a
href=
"{% url 'ops:task-list' %}"
>
{% trans '
Tasks
' %}
</a></li>
</ul>
</ul>
</li>
</li>
...
...
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