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
a822f667
Commit
a822f667
authored
Mar 09, 2017
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Fixture] 完成用户推送task
parent
c234b5b2
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
269 additions
and
311 deletions
+269
-311
forms.py
apps/assets/forms.py
+36
-33
asset.py
apps/assets/models/asset.py
+1
-1
user.py
apps/assets/models/user.py
+16
-2
_system_user.html
apps/assets/templates/assets/_system_user.html
+12
-9
system_user_create.html
apps/assets/templates/assets/system_user_create.html
+7
-0
system_user_update.html
apps/assets/templates/assets/system_user_update.html
+15
-0
utils.py
apps/assets/utils.py
+2
-0
views.py
apps/assets/views.py
+7
-2
utils.py
apps/common/utils.py
+9
-0
models.py
apps/ops/models.py
+31
-0
__init__.py
apps/ops/models/__init__.py
+0
-4
ansible.py
apps/ops/models/ansible.py
+0
-0
task.py
apps/ops/models/task.py
+0
-32
utils.py
apps/ops/models/utils.py
+0
-12
tasks.py
apps/ops/tasks.py
+87
-0
__init__.py
apps/ops/tasks/__init__.py
+0
-2
_celery_tasks.py
apps/ops/tasks/_celery_tasks.py
+0
-48
taskers.py
apps/ops/tasks/taskers.py
+0
-126
view_urls.py
apps/ops/urls/view_urls.py
+0
-4
runner.py
apps/ops/utils/runner.py
+13
-5
views.py
apps/ops/views.py
+3
-21
hands.py
apps/perms/hands.py
+0
-5
asset_permission_create_update.html
...perms/templates/perms/asset_permission_create_update.html
+1
-1
utils.py
apps/perms/utils.py
+20
-3
views.py
apps/perms/views.py
+6
-0
requirements.txt
requirements/requirements.txt
+1
-0
run_server.py
run_server.py
+2
-1
No files found.
apps/assets/forms.py
View file @
a822f667
...
...
@@ -3,7 +3,10 @@ from django import forms
from
django.utils.translation
import
gettext_lazy
as
_
from
.models
import
IDC
,
Asset
,
AssetGroup
,
AdminUser
,
SystemUser
,
Tag
from
common.utils
import
validate_ssh_private_key
,
ssh_pubkey_gen
from
common.utils
import
validate_ssh_private_key
,
ssh_pubkey_gen
,
ssh_key_gen
,
get_logger
logger
=
get_logger
(
__file__
)
class
AssetCreateForm
(
forms
.
ModelForm
):
...
...
@@ -207,59 +210,59 @@ class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
auto_generate_key
=
forms
.
BooleanField
(
initial
=
True
,
required
=
False
)
# Form field name can not start with `_`, so redefine it,
password
=
forms
.
CharField
(
widget
=
forms
.
PasswordInput
,
max_length
=
100
,
min_length
=
8
,
strip
=
True
)
password
=
forms
.
CharField
(
widget
=
forms
.
PasswordInput
,
required
=
False
,
max_length
=
100
,
min_length
=
8
,
strip
=
True
)
# Need use upload private key file except paste private key content
private_key_file
=
forms
.
FileField
(
required
=
False
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
# When update a admin user instance, initial it
if
kwargs
.
get
(
'instance'
):
initial
=
kwargs
.
get
(
'initial'
,
{})
initial
[
'assets'
]
=
kwargs
[
'instance'
]
.
assets
.
all
()
initial
[
'asset_groups'
]
=
kwargs
[
'instance'
]
.
asset_groups
.
all
()
super
(
SystemUserForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
save
(
self
,
commit
=
True
):
# Because we define custom field, so we need rewrite :method: `save`
system_user
=
super
(
SystemUserForm
,
self
)
.
save
(
commit
=
commit
)
password
=
self
.
cleaned_data
[
'password'
]
private_key_file
=
self
.
cleaned_data
[
'private_key_file'
]
private_key_file
=
self
.
cleaned_data
.
get
(
'private_key_file'
)
if
system_user
.
auth_method
==
'P'
:
if
password
:
system_user
.
password
=
password
print
(
password
)
# Todo: Validate private key file, and generate public key
# Todo: Auto generate private key and public key
if
private_key_file
:
system_user
.
private_key
=
private_key_file
.
read
()
.
strip
()
elif
system_user
.
auth_method
==
'K'
:
if
self
.
cleaned_data
[
'auto_generate_key'
]:
private_key
,
public_key
=
ssh_key_gen
(
username
=
system_user
.
name
)
logger
.
info
(
'Generate private key and public key'
)
else
:
private_key
=
private_key_file
.
read
()
.
strip
()
public_key
=
ssh_pubkey_gen
(
private_key
=
private_key
)
system_user
.
private_key
=
private_key
system_user
.
public_key
=
public_key
system_user
.
save
()
return
self
.
instance
# Todo: check valid
# def clean_private_key_file(self):
# if not self.cleaned_data['auto_generate_key']:
# if not self.cleaned_data['private_key_file']:
# raise forms.ValidationError(_('Private key required'))
# def clean_password(self):
# if self.cleaned_data['auth_method'] == 'P':
# if not self.cleaned_data['password']:
# raise forms.ValidationError(_('Password required'))
# return self.cleaned_data['password']
# def clean(self):
# password = self.cleaned_data['password']
# private_key_file = self.cleaned_data.get('private_key_file', '')
#
# if not (password or private_key_file):
# raise forms.ValidationError(_('Password and private key file must be input one'))
def
clean_private_key_file
(
self
):
if
self
.
data
[
'auth_method'
]
==
'K'
and
\
not
self
.
cleaned_data
[
'auto_generate_key'
]:
if
not
self
.
cleaned_data
[
'private_key_file'
]:
raise
forms
.
ValidationError
(
_
(
'Private key required'
))
else
:
key_string
=
self
.
cleaned_data
[
'private_key_file'
]
.
read
()
self
.
cleaned_data
[
'private_key_file'
]
.
seek
(
0
)
if
not
validate_ssh_private_key
(
key_string
):
raise
forms
.
ValidationError
(
_
(
'Private key invalid'
))
return
self
.
cleaned_data
[
'private_key_file'
]
def
clean_password
(
self
):
if
self
.
data
[
'auth_method'
]
==
'P'
:
if
not
self
.
cleaned_data
.
get
(
'password'
):
raise
forms
.
ValidationError
(
_
(
'Password required'
))
return
self
.
cleaned_data
[
'password'
]
class
Meta
:
model
=
SystemUser
fields
=
[
'name'
,
'username'
,
'protocol'
,
'auto_generate_key'
,
'password'
,
'private_key_file'
,
'auth_method'
,
'auto_push'
,
'sudo'
,
'comment'
,
'shell'
,
'home'
,
'uid'
,
'name'
,
'username'
,
'protocol'
,
'auto_generate_key'
,
'password'
,
'private_key_file'
,
'auth_method'
,
'auto_push'
,
'sudo'
,
'comment'
,
'shell'
,
'home'
,
'uid'
,
]
widgets
=
{
'name'
:
forms
.
TextInput
(
attrs
=
{
'placeholder'
:
_
(
'Name'
)}),
...
...
apps/assets/models/asset.py
View file @
a822f667
...
...
@@ -71,7 +71,7 @@ class Asset(models.Model):
tags
=
models
.
ManyToManyField
(
'Tag'
,
related_name
=
'assets'
,
blank
=
True
,
verbose_name
=
_
(
'Tags'
))
def
__unicode__
(
self
):
return
'
%
(ip)
s:
%(port)
s'
%
{
'ip'
:
self
.
ip
,
'port'
:
self
.
port
}
return
'
%
s <
%
s:
%
s>'
%
(
self
.
hostname
,
self
.
ip
,
self
.
port
)
__str__
=
__unicode__
...
...
apps/assets/models/user.py
View file @
a822f667
...
...
@@ -116,14 +116,13 @@ class SystemUser(models.Model):
)
name
=
models
.
CharField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Name'
))
username
=
models
.
CharField
(
max_length
=
16
,
verbose_name
=
_
(
'Username'
))
_password
=
models
.
CharField
(
max_length
=
256
,
blank
=
True
,
verbose_name
=
_
(
'Password'
))
_password
=
models
.
CharField
(
max_length
=
256
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Password'
))
protocol
=
models
.
CharField
(
max_length
=
16
,
choices
=
PROTOCOL_CHOICES
,
default
=
'ssh'
,
verbose_name
=
_
(
'Protocol'
))
_private_key
=
models
.
CharField
(
max_length
=
4096
,
blank
=
True
,
verbose_name
=
_
(
'SSH private key'
))
_public_key
=
models
.
CharField
(
max_length
=
4096
,
blank
=
True
,
verbose_name
=
_
(
'SSH public key'
))
auth_method
=
models
.
CharField
(
choices
=
AUTH_METHOD_CHOICES
,
default
=
'K'
,
max_length
=
1
,
verbose_name
=
_
(
'Auth method'
))
auto_push
=
models
.
BooleanField
(
default
=
True
,
verbose_name
=
_
(
'Auto push'
))
auto_update
=
models
.
BooleanField
(
default
=
True
,
verbose_name
=
_
(
'Auto update pass/key'
))
sudo
=
models
.
TextField
(
max_length
=
4096
,
default
=
'/user/bin/whoami'
,
verbose_name
=
_
(
'Sudo'
))
shell
=
models
.
CharField
(
max_length
=
64
,
default
=
'/bin/bash'
,
verbose_name
=
_
(
'Shell'
))
home
=
models
.
CharField
(
max_length
=
64
,
blank
=
True
,
verbose_name
=
_
(
'Home'
))
...
...
@@ -137,7 +136,9 @@ class SystemUser(models.Model):
@property
def
password
(
self
):
if
self
.
_password
:
return
signer
.
unsign
(
self
.
_password
)
return
None
@password.setter
def
password
(
self
,
password_raw
):
...
...
@@ -145,7 +146,9 @@ class SystemUser(models.Model):
@property
def
private_key
(
self
):
if
self
.
_private_key
:
return
signer
.
unsign
(
self
.
_private_key
)
return
None
@private_key.setter
def
private_key
(
self
,
private_key_raw
):
...
...
@@ -174,6 +177,17 @@ class SystemUser(models.Model):
assets
=
set
(
self
.
assets
.
all
())
|
self
.
get_assets_inherit_from_asset_groups
()
return
list
(
assets
)
def
_to_secret_json
(
self
):
"""Push system user use it"""
return
{
'name'
:
self
.
name
,
'username'
:
self
.
username
,
'shell'
:
self
.
shell
,
'sudo'
:
self
.
sudo
,
'password'
:
self
.
password
,
'public_key'
:
self
.
public_key
}
@property
def
assets_amount
(
self
):
return
self
.
assets
.
count
()
...
...
apps/assets/templates/assets/
system_user_create_update
.html
→
apps/assets/templates/assets/
_system_user
.html
View file @
a822f667
...
...
@@ -34,12 +34,18 @@
{% endif %}
<form
enctype=
"multipart/form-data"
method=
"post"
class=
"form-horizontal"
action=
""
>
{% csrf_token %}
{% if form.non_field_errors %}
<div
class=
"alert alert-danger"
>
{{ form.non_field_errors }}
</div>
{% endif %}
<h3>
{% trans 'Basic' %}
</h3>
{{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }}
{{ form.protocol|bootstrap_horizontal }}
<h3>
{% trans 'Auth' %}
</h3>
{{ form.auth_method|bootstrap_horizontal }}
{% block auth %}
<div
class=
"password-auth hidden"
>
{{ form.password|bootstrap_horizontal }}
</div>
...
...
@@ -54,6 +60,7 @@
{{ form.private_key_file|bootstrap_horizontal }}
</div>
</div>
{% endblock %}
<div
class=
"form-group"
>
<label
for=
"{{ form.as_push.id_for_label }}"
class=
"col-sm-2 control-label"
>
{% trans 'Auto push' %}
</label>
<div
class=
"col-sm-8"
>
...
...
@@ -88,16 +95,19 @@
$
(
'.password-auth'
).
removeClass
(
'hidden'
);
$
(
'.public-key-auth'
).
addClass
(
'hidden'
);
$
(
'#'
+
'{{ form.password.id_for_label }}'
).
attr
(
'required'
,
'required'
);
$
(
'#'
+
'{{ form.password.id_for_label }}'
).
removeAttr
(
'disabled'
);
}
else
if
(
$
(
auth_method
).
val
()
==
'K'
)
{
$
(
'.password-auth'
).
addClass
(
'hidden'
);
$
(
'.public-key-auth'
).
removeClass
(
'hidden'
);
$
(
'#'
+
'{{ form.password.id_for_label }}'
).
removeAttr
(
'required'
);
$
(
'#'
+
'{{ form.password.id_for_label }}'
).
attr
(
'disabled'
,
'disabled'
);
if
(
$
(
auto_generate_key
).
prop
(
'checked'
)){
$
(
'#'
+
'{{ form.private_key_file.id_for_label }}'
).
closest
(
'.form-group'
).
addClass
(
'hidden'
);
}
else
{
$
(
'#'
+
'{{ form.private_key_file.id_for_label }}'
).
closest
(
'.form-group'
).
removeClass
(
'hidden'
);
{
#
$
(
'#'
+
'{{ form.private_key_file.id_for_label }}'
).
attr
(
'required'
,
'required'
);
#
}
$
(
'#'
+
'{{ form.private_key_file.id_for_label }}'
).
closest
(
'.form-group input'
).
attr
(
'required'
,
'required'
);
}
}
}
...
...
@@ -110,14 +120,8 @@
$
(
auto_generate_key
).
change
(
function
()
{
authMethodDisplay
();
});
})
if
(
$
(
'#'
+
'{{ form.protocol.id_for_label }}'
).
val
()
==
'telnet'
)
{
$
(
'#'
+
'{{ form.auto_generate_key.id_for_label }}'
).
closest
(
'.form-group'
).
remove
();
$
(
'#'
+
'{{ form.private_key_file.id_for_label }}'
).
closest
(
'.form-group'
).
remove
();
$
(
'#'
+
'{{ form.auto_push.id_for_label }}'
).
closest
(
'.form-group'
).
remove
();
$
(
'#'
+
'{{ form.auto_update.id_for_label }}'
).
closest
(
'.form-group'
).
remove
();
}
})
</script>
{% endblock %}
\ No newline at end of file
apps/assets/templates/assets/system_user_create.html
0 → 100644
View file @
a822f667
{% extends 'assets/_system_user.html' %}
{% load i18n %}
{% load static %}
{% block auth %}
{{ block.super }}
{% endblock %}
apps/assets/templates/assets/system_user_update.html
0 → 100644
View file @
a822f667
{% extends 'assets/_system_user.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block auth %}
<div
class=
"password-auth hidden"
>
{{ form.password|bootstrap_horizontal }}
</div>
<div
class=
"public-key-auth"
>
<div>
{{ form.private_key_file|bootstrap_horizontal }}
</div>
</div>
{% endblock %}
apps/assets/utils.py
View file @
a822f667
...
...
@@ -4,6 +4,7 @@ from rest_framework import serializers
from
models
import
Tag
from
django.views.generic.edit
import
CreateView
class
CreateAssetTagsMiXin
(
CreateView
):
def
get_form_kwargs
(
self
):
tags_list
=
self
.
request
.
POST
.
getlist
(
'tags'
)
...
...
@@ -30,6 +31,7 @@ class CreateAssetTagsMiXin(CreateView):
})
return
kwargs
class
UpdateAssetTagsMiXin
(
CreateAssetTagsMiXin
):
def
get_form_kwargs
(
self
):
kwargs
=
super
(
UpdateAssetTagsMiXin
,
self
)
.
get_form_kwargs
()
...
...
apps/assets/views.py
View file @
a822f667
...
...
@@ -9,6 +9,7 @@ from openpyxl import load_workbook
from
django.utils.translation
import
ugettext
as
_
from
django.conf
import
settings
from
django.db.models
import
Q
from
django.db
import
transaction
from
django.db
import
IntegrityError
from
django.views.generic
import
TemplateView
,
ListView
,
View
from
django.views.generic.edit
import
CreateView
,
DeleteView
,
FormView
,
UpdateView
...
...
@@ -521,9 +522,13 @@ class SystemUserListView(AdminUserRequiredMixin, TemplateView):
class
SystemUserCreateView
(
AdminUserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
model
=
SystemUser
form_class
=
forms
.
SystemUserForm
template_name
=
'assets/system_user_create
_update
.html'
template_name
=
'assets/system_user_create.html'
success_url
=
reverse_lazy
(
'assets:system-user-list'
)
@transaction.atomic
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
return
super
(
SystemUserCreateView
,
self
)
.
post
(
request
,
*
args
,
**
kwargs
)
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Assets'
),
...
...
@@ -549,7 +554,7 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
class
SystemUserUpdateView
(
AdminUserRequiredMixin
,
UpdateView
):
model
=
SystemUser
form_class
=
forms
.
SystemUserForm
template_name
=
'assets/system_user_
create_
update.html'
template_name
=
'assets/system_user_update.html'
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
...
...
apps/common/utils.py
View file @
a822f667
...
...
@@ -16,6 +16,7 @@ import calendar
import
threading
import
paramiko
from
passlib.hash
import
sha512_crypt
import
sshpubkeys
from
itsdangerous
import
TimedJSONWebSignatureSerializer
,
JSONWebSignatureSerializer
,
\
BadSignature
,
SignatureExpired
...
...
@@ -322,4 +323,11 @@ def make_signature(access_key_secret, date=None):
return
content_md5
(
data
)
def
encrypt_password
(
password
):
from
passlib.hash
import
sha512_crypt
if
password
:
return
sha512_crypt
.
using
(
rounds
=
5000
)
.
hash
(
password
)
return
None
signer
=
Signer
()
\ No newline at end of file
apps/ops/models.py
0 → 100644
View file @
a822f667
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
,
absolute_import
import
logging
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
__all__
=
[
"TaskRecord"
]
logger
=
logging
.
getLogger
(
__name__
)
class
TaskRecord
(
models
.
Model
):
uuid
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
'UUID'
),
primary_key
=
True
)
name
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Name'
))
date_start
=
models
.
DateTimeField
(
auto_now_add
=
True
,
verbose_name
=
_
(
'Start time'
))
date_finished
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'End time'
))
is_finished
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Is finished'
))
is_success
=
models
.
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'Is success'
))
assets
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Assets'
))
result
=
models
.
TextField
(
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Task result'
))
def
__unicode__
(
self
):
return
"
%
s"
%
self
.
uuid
@property
def
total_assets
(
self
):
return
self
.
assets
.
split
(
','
)
apps/ops/models/__init__.py
deleted
100644 → 0
View file @
c234b5b2
from
ansible
import
*
from
utils
import
*
from
task
import
*
apps/ops/models/ansible.py
deleted
100644 → 0
View file @
c234b5b2
This diff is collapsed.
Click to expand it.
apps/ops/models/task.py
deleted
100644 → 0
View file @
c234b5b2
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
,
absolute_import
import
logging
from
uuid
import
uuid4
from
assets.models
import
Asset
from
ops.models
import
TaskRecord
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
__all__
=
[
"Task"
]
class
Task
(
models
.
Model
):
"""
Ansible 的Task
"""
name
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
'Task name'
))
module_name
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
'Task module'
))
module_args
=
models
.
CharField
(
max_length
=
512
,
blank
=
True
,
verbose_name
=
_
(
"Module args"
))
def
__unicode__
(
self
):
return
"
%
s"
%
self
.
name
class
Play
(
models
.
Model
):
"""
Playbook 模板, 定义好Template后生成 Playbook
"""
apps/ops/models/utils.py
deleted
100644 → 0
View file @
c234b5b2
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
from
ansible
import
*
__all__
=
[
"generate_fake"
]
def
generate_fake
():
for
cls
in
(
TaskRecord
,
AnsiblePlay
,
AnsibleTask
,
AnsibleHostResult
):
cls
.
generate_fake
()
\ No newline at end of file
apps/ops/tasks.py
0 → 100644
View file @
a822f667
# coding: utf-8
from
__future__
import
absolute_import
,
unicode_literals
import
time
from
django.utils
import
timezone
from
celery
import
shared_task
from
common.utils
import
get_logger
,
encrypt_password
from
.utils.runner
import
AdHocRunner
from
.models
import
TaskRecord
logger
=
get_logger
(
__file__
)
@shared_task
(
name
=
"get_assets_hardware_info"
)
def
get_assets_hardware_info
(
self
,
assets
):
task_tuple
=
(
(
'setup'
,
''
),
)
hoc
=
AdHocRunner
(
assets
)
return
hoc
.
run
(
task_tuple
)
@shared_task
(
name
=
"asset_test_ping_check"
)
def
asset_test_ping_check
(
assets
):
task_tuple
=
(
(
'ping'
,
''
),
)
hoc
=
AdHocRunner
(
assets
)
result
=
hoc
.
run
(
task_tuple
)
return
result
[
'contacted'
]
.
keys
(),
result
[
'dark'
]
.
keys
()
@shared_task
(
bind
=
True
)
def
push_users
(
self
,
assets
,
users
):
"""
user: {
username: xxx,
shell: /bin/bash,
password: 'staf',
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
:
logger
.
debug
(
'Push user: {}'
.
format
(
user
))
# 添加用户, 设置公钥, 设置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'
,
"name=/etc/sudoers state=present regexp='^{0} ALL=(ALL)' "
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
"validate='visudo -cf
%
s'"
.
format
(
user
[
'username'
],
user
.
get
(
'sudo'
,
'/bin/whoami'
)
))
])
record
=
TaskRecord
(
name
=
'Push user'
,
uuid
=
self
.
request
.
id
,
date_start
=
timezone
.
now
(),
assets
=
','
.
join
(
asset
[
'hostname'
]
for
asset
in
assets
))
record
.
save
()
logger
.
info
(
'Runner start {0}'
.
format
(
timezone
.
now
()))
hoc
=
AdHocRunner
(
assets
)
_
=
hoc
.
run
(
task_tuple
)
logger
.
info
(
'Runner complete {0}'
.
format
(
timezone
.
now
()))
result_clean
=
hoc
.
clean_result
()
record
.
date_finished
=
timezone
.
now
()
record
.
is_finished
=
True
if
len
(
result_clean
[
'failed'
])
==
0
:
record
.
is_success
=
True
else
:
record
.
is_success
=
False
record
.
result
=
result_clean
record
.
save
()
return
result_clean
apps/ops/tasks/__init__.py
deleted
100644 → 0
View file @
c234b5b2
from
taskers
import
*
\ No newline at end of file
apps/ops/tasks/_celery_tasks.py
deleted
100644 → 0
View file @
c234b5b2
from
__future__
import
absolute_import
,
unicode_literals
from
celery
import
shared_task
from
common
import
celery_app
from
ops.utils.ansible_api
import
Options
,
ADHocRunner
@shared_task
(
name
=
"get_asset_hardware_info"
)
def
get_asset_hardware_info
(
task_name
,
task_uuid
,
*
assets
):
conf
=
Options
()
play_source
=
{
"name"
:
"Get host hardware information"
,
"hosts"
:
"default"
,
"gather_facts"
:
"no"
,
"tasks"
:
[
dict
(
action
=
dict
(
module
=
'setup'
))
]
}
hoc
=
ADHocRunner
(
conf
,
play_source
,
*
assets
)
ext_code
,
result
=
hoc
.
run
(
task_name
,
task_uuid
)
return
ext_code
,
result
@shared_task
(
name
=
"asset_test_ping_check"
)
def
asset_test_ping_check
(
task_name
,
task_uuid
,
*
assets
):
conf
=
Options
()
play_source
=
{
"name"
:
"Test host connection use ping"
,
"hosts"
:
"default"
,
"gather_facts"
:
"no"
,
"tasks"
:
[
dict
(
action
=
dict
(
module
=
'ping'
))
]
}
hoc
=
ADHocRunner
(
conf
,
play_source
,
*
assets
)
ext_code
,
result
=
hoc
.
run
(
task_name
,
task_uuid
)
return
ext_code
,
result
@shared_task
(
name
=
"add_user_to_assert"
)
def
add_user_to_asset
():
pass
@celery_app.task
(
name
=
'hello-world'
)
def
hello
():
print
(
'hello world!'
)
apps/ops/tasks/taskers.py
deleted
100644 → 0
View file @
c234b5b2
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
from
ops.tasks
import
_celery_tasks
from
ops.models
import
TaskRecord
from
uuid
import
uuid1
from
celery.result
import
AsyncResult
__all__
=
[
"get_result"
,
"start_get_hardware_info"
,
"start_ping_test"
,
"get_hardware_info"
,
"get_ping_test"
]
def
get_result
(
task_id
):
result
=
AsyncResult
(
task_id
)
if
result
.
ready
():
return
{
"Completed"
:
True
,
"data"
:
result
.
get
()}
else
:
return
{
"Completed"
:
False
,
"data"
:
None
}
def
__get_result_by_tasker_id
(
tasker_uuid
,
deal_method
):
tasker
=
TaskRecord
.
objects
.
get
(
uuid
=
tasker_uuid
)
total
=
tasker
.
total_hosts
total_len
=
len
(
total
)
host_results
=
[]
# 存储数据
for
play
in
tasker
.
plays
.
all
():
for
t
in
play
.
tasks
.
all
():
task
=
{
'name'
:
t
.
name
,
'uuid'
:
t
.
uuid
,
'percentage'
:
0
,
'completed'
:
{
'success'
:
{},
'failed'
:
{}}}
completed
=
[]
count
=
0
for
h
in
t
.
host_results
.
all
():
completed
.
append
(
h
.
name
)
count
+=
1
if
h
.
is_success
:
result
=
getattr
(
h
,
deal_method
)
if
result
.
get
(
'msg'
)
is
None
:
task
[
'completed'
][
'success'
][
h
.
name
]
=
result
.
get
(
'data'
)
else
:
task
[
'completed'
][
'failed'
][
h
.
name
]
=
result
.
get
(
'msg'
)
else
:
task
[
'completed'
][
'failed'
][
h
.
name
]
=
h
.
failed_msg
# 计算进度
task
[
'percentage'
]
=
float
(
count
*
100
/
total_len
)
task
[
'waited'
]
=
list
(
set
(
total
)
-
set
(
completed
))
host_results
.
append
(
task
)
return
host_results
def
start_get_hardware_info
(
*
assets
):
name
=
"Get host hardware information"
uuid
=
"tasker-"
+
uuid1
()
.
hex
_celery_tasks
.
get_asset_hardware_info
.
delay
(
name
,
uuid
,
*
assets
)
return
uuid
def
__get_hardware_info
(
tasker_uuid
):
return
__get_result_by_tasker_id
(
tasker_uuid
,
'deal_setup'
)
def
get_hardware_info
(
tasker_uuid
):
"""
:param assets: 资产列表
:return: 返回数据结构样列
{u'data': [{u'completed': {
u'failed': {u'192.168.232.135': u'Authentication failure.'},
u'success': {u'192.168.1.119': {u'cpu': u'GenuineIntel Intel Xeon E312xx (Sandy Bridge) 6
\u6838
',
u'disk': {<device_name>: <device_detail_dict>},
u'env': {<env_name>: <env_value>},
u'interface': {<interface_name>: <interface_detail_dict>},
u'mem': 3951,
u'os': u'Ubuntu 16.04(xenial)',
u'sn': u'NA'}}},
u'name': u'',
u'percentage': 100.0,
u'uuid': u'87cfedfe-ba55-44ff-bc43-e7e73b869ca1',
u'waited': []}
],
u'msg': None}
"""
try
:
return
{
"msg"
:
None
,
"data"
:
__get_hardware_info
(
tasker_uuid
)}
except
Exception
as
e
:
return
{
"msg"
:
"query data failed!,
%
s"
%
e
.
message
,
"data"
:
None
}
def
start_ping_test
(
*
assets
):
name
=
"Test host connection"
uuid
=
"tasker-"
+
uuid1
()
.
hex
_celery_tasks
.
asset_test_ping_check
.
delay
(
name
,
uuid
,
*
assets
)
return
uuid
def
__get_ping_test
(
tasker_uuid
):
return
__get_result_by_tasker_id
(
tasker_uuid
,
'deal_ping'
)
def
get_ping_test
(
tasker_uuid
):
"""
:param assets: 资产列表
:return: 返回数据结构样列
{u'data': [{u'completed': {
u'failed': {u'192.168.232.135': u'Authentication failure.'},
u'success': {u'192.168.1.119': {u'success': True}}},
u'name': u'',
u'percentage': 100.0,
u'uuid': u'3e6e0d3b-bee0-4383-b19e-bec6ba55d346',
u'waited': []}
],
u'msg': None}
"""
try
:
return
{
"msg"
:
None
,
"data"
:
__get_ping_test
(
tasker_uuid
)}
except
Exception
as
e
:
return
{
"msg"
:
"query data failed!,
%
s"
%
e
.
message
,
"data"
:
None
}
apps/ops/urls/view_urls.py
View file @
a822f667
...
...
@@ -10,7 +10,4 @@ __all__ = ["urlpatterns"]
urlpatterns
=
[
# TResource Task url
url
(
r'^task/list$'
,
page_view
.
TaskListView
.
as_view
(),
name
=
'page-task-list'
),
url
(
r'^task/create$'
,
page_view
.
TaskCreateView
.
as_view
(),
name
=
'page-task-create'
),
url
(
r'^task/(?P<pk>[0-9]+)/detail$'
,
page_view
.
TaskDetailView
.
as_view
(),
name
=
'page-task-detail'
),
url
(
r'^task/(?P<pk>[0-9]+)/update$'
,
page_view
.
TaskUpdateView
.
as_view
(),
name
=
'page-task-update'
),
]
\ No newline at end of file
apps/ops/utils/runner.py
View file @
a822f667
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
import
os
import
sys
from
collections
import
namedtuple
,
defaultdict
from
ansible.executor.task_queue_manager
import
TaskQueueManager
...
...
@@ -249,12 +249,20 @@ class AdHocRunner(object):
self
.
loader
.
cleanup_all_tmp_files
()
def
clean_result
(
self
):
result
=
defaultdict
(
dict
)
for
host
,
msgs
in
self
.
results_callback
.
result_q
[
'contacted'
]
.
items
():
result
[
host
][
'success'
]
=
len
(
msgs
)
"""
:return: {
"success": ['hostname',],
"failed": [{'hostname': 'msg'}, {}],
}
"""
result
=
{
'success'
:
[],
'failed'
:
[]}
for
host
in
self
.
results_callback
.
result_q
[
'contacted'
]:
result
[
'success'
]
.
append
(
host
)
for
host
,
msgs
in
self
.
results_callback
.
result_q
[
'dark'
]
.
items
():
result
[
host
][
'failed'
]
=
len
(
msgs
)
msg
=
'
\n
'
.
join
([
'{}: {}'
.
format
(
msg
.
get
(
'invocation'
,
{})
.
get
(
'module_name'
),
msg
.
get
(
'msg'
,
''
))
for
msg
in
msgs
])
result
[
'failed'
]
.
append
({
host
:
msg
})
return
result
...
...
apps/ops/views.py
View file @
a822f667
...
...
@@ -2,18 +2,15 @@
from
__future__
import
unicode_literals
from
django.conf
import
settings
from
django.views.generic.list
import
ListView
,
MultipleObjectMixin
from
django.views.generic.edit
import
CreateView
,
DeleteView
,
UpdateView
from
django.views.generic.detail
import
DetailView
,
SingleObjectMixin
from
django.views.generic.list
import
ListView
from
users.utils
import
AdminUserRequiredMixin
from
ops.utils.mixins
import
CreateSudoPrivilegesMixin
,
ListSudoPrivilegesMixin
from
.models
import
Task
from
.models
import
TaskRecord
class
TaskListView
(
AdminUserRequiredMixin
,
ListView
):
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
model
=
Task
model
=
Task
Record
context_object_name
=
'tasks'
template_name
=
'task/list.html'
...
...
@@ -25,18 +22,3 @@ class TaskListView(AdminUserRequiredMixin, ListView):
kwargs
.
update
(
context
)
return
super
(
TaskListView
,
self
)
.
get_context_data
(
**
kwargs
)
class
TaskCreateView
(
AdminUserRequiredMixin
,
CreateView
):
model
=
Task
template_name
=
'task/create.html'
class
TaskUpdateView
(
AdminUserRequiredMixin
,
UpdateView
):
model
=
Task
template_name
=
'task/update.html'
class
TaskDetailView
(
DetailView
):
model
=
Task
context_object_name
=
'task'
template_name
=
'task/detail.html'
apps/perms/hands.py
View file @
a822f667
...
...
@@ -7,9 +7,4 @@ from assets.models import Asset, AssetGroup, SystemUser
from
assets.serializers
import
AssetGrantedSerializer
,
AssetGroupSerializer
def
push_system_user
(
assets
,
system_user
):
print
(
'Push system user
%
s'
%
system_user
.
name
)
for
asset
in
assets
:
print
(
'
\t
Asset:
%
s'
%
asset
.
ip
)
apps/perms/templates/perms/asset_permission_create_update.html
View file @
a822f667
...
...
@@ -38,7 +38,7 @@
{{ form.user_groups|bootstrap_horizontal }}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Asset' %}
</h3>
{{ form.assets|bootstrap_horizontal }}
{{ form.assets|bootstrap_horizontal
|safe
}}
{{ form.asset_groups|bootstrap_horizontal }}
{{ form.system_users |bootstrap_horizontal }}
<div
class=
"hr-line-dashed"
></div>
...
...
apps/perms/utils.py
View file @
a822f667
...
...
@@ -2,9 +2,11 @@
from
__future__
import
absolute_import
,
unicode_literals
from
common.utils
import
setattr_bulk
from
.hands
import
User
,
UserGroup
,
Asset
,
AssetGroup
,
SystemUser
,
\
push_system_user
from
common.utils
import
setattr_bulk
,
get_logger
from
ops.tasks
import
push_users
from
.hands
import
User
,
UserGroup
,
Asset
,
AssetGroup
,
SystemUser
logger
=
get_logger
(
__file__
)
def
get_user_group_granted_asset_groups
(
user_group
):
...
...
@@ -220,6 +222,19 @@ def get_users_granted_in_asset_group(asset):
pass
def
push_system_user
(
assets
,
system_user
):
logger
.
info
(
'Push system user
%
s'
%
system_user
.
name
)
for
asset
in
assets
:
logger
.
info
(
'
\t
Asset:
%
s'
%
asset
.
ip
)
if
not
assets
:
return
None
assets
=
[
asset
.
_to_secret_json
()
for
asset
in
assets
]
system_user
=
system_user
.
_to_secret_json
()
task
=
push_users
.
delay
(
assets
,
system_user
)
return
task
.
id
def
associate_system_users_and_assets
(
system_users
,
assets
,
asset_groups
):
"""关联系统用户和资产, 目的是保存它们的关系, 然后新加入的资产或系统
用户时,推送系统用户到资产
...
...
@@ -242,3 +257,5 @@ def associate_system_users_and_assets(system_users, assets, asset_groups):
)
system_user
.
assets
.
add
(
*
(
tuple
(
assets_all
)))
push_system_user
(
assets_need_push
,
system_user
)
apps/perms/views.py
View file @
a822f667
# ~*~ coding: utf-8 ~*~
from
__future__
import
unicode_literals
,
absolute_import
import
functools
from
django.utils.translation
import
ugettext
as
_
from
django.db
import
transaction
from
django.conf
import
settings
from
django.db.models
import
Q
from
django.views.generic
import
ListView
,
CreateView
,
UpdateView
...
...
@@ -65,6 +67,10 @@ class AssetPermissionCreateView(AdminUserRequiredMixin,
template_name
=
'perms/asset_permission_create_update.html'
success_url
=
reverse_lazy
(
'perms:asset-permission-list'
)
@transaction.atomic
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
return
super
(
AssetPermissionCreateView
,
self
)
.
post
(
request
,
*
args
,
**
kwargs
)
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Perms'
),
...
...
requirements/requirements.txt
View file @
a822f667
...
...
@@ -19,3 +19,4 @@ itsdangerous==0.24
tornado==4.4.2
eventlet==0.20.1
django-filter==1.0.0
passlib==1.7.1
run_server.py
View file @
a822f667
...
...
@@ -29,8 +29,9 @@ def start_django():
def
start_celery
():
os
.
chdir
(
apps_dir
)
os
.
environ
.
setdefault
(
'C_FORCE_ROOT'
,
'1'
)
os
.
environ
.
setdefault
(
'PYTHONOPTIMIZE'
,
1
)
print
(
'start celery'
)
subprocess
.
call
(
'celery -A common worker -
P eventlet -s /tmp/celerybeat-schedule -l info
'
,
shell
=
True
)
subprocess
.
call
(
'celery -A common worker -
s /tmp/celerybeat-schedule -l debug
'
,
shell
=
True
)
def
main
():
...
...
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