提交 a822f667 编写于 作者: baltery's avatar baltery

[Fixture] 完成用户推送task

上级 c234b5b2
......@@ -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')}),
......
......@@ -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__
......
......@@ -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):
return signer.unsign(self._password)
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):
return signer.unsign(self._private_key)
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()
......
......@@ -34,14 +34,20 @@
{% 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 }}
{{ form.password|bootstrap_horizontal }}
</div>
<div class="public-key-auth">
<div class="form-group">
......@@ -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
{% extends 'assets/_system_user.html' %}
{% load i18n %}
{% load static %}
{% block auth %}
{{ block.super }}
{% endblock %}
{% 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 %}
......@@ -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()
......
......@@ -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 = {
......
......@@ -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
# ~*~ 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(',')
from ansible import *
from utils import *
from task import *
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals, absolute_import
import logging
import json
from django.db import models
from django.utils.translation import ugettext_lazy as _
__all__ = ["TaskRecord", "AnsiblePlay", "AnsibleTask", "AnsibleHostResult"]
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'))
start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start Time'))
end = models.DateTimeField(blank=True, null=True, verbose_name=_('End Time'))
exit_code = models.IntegerField(default=0, verbose_name=_('Exit Code'))
completed = models.BooleanField(default=False, verbose_name=_('Is Completed'))
hosts = models.TextField(blank=True, null=True, verbose_name=_('Hosts'))
def __unicode__(self):
return "%s" % self.uuid
@property
def total_hosts(self):
return self.hosts.split(',')
@classmethod
def generate_fake(cls, count=20):
from random import seed
from uuid import uuid4
import forgery_py
seed()
for i in range(count):
tasker = cls(uuid=str(uuid4()),
name=forgery_py.name.full_name(),
)
try:
tasker.save()
logger.debug('Generate fake tasker: %s' % tasker.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class AnsiblePlay(models.Model):
tasker = models.ForeignKey(TaskRecord, related_name='plays', blank=True, null=True)
uuid = models.CharField(max_length=128, verbose_name=_('UUID'), primary_key=True)
name = models.CharField(max_length=128, verbose_name=_('Name'))
def __unicode__(self):
return "%s<%s>" % (self.name, self.uuid)
def to_dict(self):
return {"uuid": self.uuid, "name": self.name}
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
from uuid import uuid4
import forgery_py
seed()
for i in range(count):
play = cls(uuid=str(uuid4()),
name=forgery_py.name.full_name(),
)
try:
play.tasker = choice(TaskRecord.objects.all())
play.save()
logger.debug('Generate fake play: %s' % play.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class AnsibleTask(models.Model):
play = models.ForeignKey(AnsiblePlay, related_name='tasks', blank=True, null=True)
uuid = models.CharField(max_length=128, verbose_name=_('UUID'), primary_key=True)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
def __unicode__(self):
return "%s<%s>" % (self.name, self.uuid)
def to_dict(self):
return {"uuid": self.uuid, "name": self.name}
def failed(self):
pass
def success(self):
pass
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
from uuid import uuid4
import forgery_py
seed()
for i in range(count):
task = cls(uuid=str(uuid4()),
name=forgery_py.name.full_name(),
)
try:
task.play = choice(AnsiblePlay.objects.all())
task.save()
logger.debug('Generate fake play: %s' % task.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
class AnsibleHostResult(models.Model):
task = models.ForeignKey(AnsibleTask, related_name='host_results', blank=True, null=True)
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
success = models.TextField(blank=True, verbose_name=_('Success'))
skipped = models.TextField(blank=True, verbose_name=_('Skipped'))
failed = models.TextField(blank=True, verbose_name=_('Failed'))
unreachable = models.TextField(blank=True, verbose_name=_('Unreachable'))
no_host = models.TextField(blank=True, verbose_name=_('NoHost'))
def __unicode__(self):
return "%s %s<%s>" % (self.name, str(self.is_success), self.task.uuid)
@property
def is_failed(self):
if self.failed or self.unreachable or self.no_host:
return True
return False
@property
def is_success(self):
return not self.is_failed
@property
def _success_data(self):
if self.success:
return json.loads(self.success)
elif self.skipped:
return json.loads(self.skipped)
@property
def _failed_data(self):
if self.failed:
return json.loads(self.failed)
elif self.unreachable:
return json.loads(self.unreachable)
elif self.no_host:
return {"msg": self.no_host}
@property
def failed_msg(self):
return self._failed_data.get("msg")
@staticmethod
def __filter_disk(ansible_devices, exclude_devices):
"""
过滤磁盘设备,丢弃掉不需要的设备
:param ansible_devices: 对应的facts字段
:param exclude_devices: <list> 一个需要被丢弃的设备,匹配规则是startwith, 比如需要丢弃sr0子类的 ['sr']
:return: <dict> 过滤获取的结果
"""
for start_str in exclude_devices:
for key in ansible_devices.keys():
if key.startswith(start_str):
ansible_devices.pop(key)
return ansible_devices
@staticmethod
def __filter_interface(ansible_interfaces, exclude_interface):
"""
过滤网卡设备,丢弃掉不需要的网卡, 比如lo
:param ansible_interface: 对应的facts字段
:param exclude_interface: <list> 一个需要被丢弃的设备,匹配规则是startwith, 比如需要丢弃lo子类的 ['lo']
:return: <dict> 过滤获取的结果
"""
for interface in ansible_interfaces:
for start_str in exclude_interface:
if interface.startswith(start_str):
i = ansible_interfaces.index(interface)
ansible_interfaces.pop(i)
return ansible_interfaces
@staticmethod
def __gather_interface(facts, interfaces):
"""
收集所有interface的具体信息
:param facts: ansible faces
:param interfaces: 需要收集的intreface列表
:return: <dict> interface的详情
"""
result = {}
for key in interfaces:
gather_key = "ansible_" + key
if gather_key in facts.keys():
result[key] = facts.get(gather_key)
return result
def __deal_setup(self):
"""
处理ansible setup模块收集到的数据,提取资产需要的部分
:return: <dict> {"msg": <str>, "data": <dict>}, 注意msg是异常信息, 有msg时 data为None
"""
result = self._success_data
module_name = result['invocation'].get('module_name') if result.get('invocation') else None
if module_name is not None:
if module_name != "setup":
return {"msg": "the property only for ansible setup module result!, can't support other module", "data":None}
else:
data = {}
facts =result.get('ansible_facts')
interfaces = self.__filter_interface(facts.get('ansible_interfaces'), ['lo'])
cpu_describe = "%s %s" % (facts.get('ansible_processor')[0], facts.get('ansible_processor')[1]) if len(facts.get('ansible_processor')) >= 2 else ""
data['sn'] = facts.get('ansible_product_serial')
data['env'] = facts.get('ansible_env')
data['os'] = "%s %s(%s)" % (facts.get('ansible_distribution'),
facts.get('ansible_distribution_version'),
facts.get('ansible_distribution_release'))
data['mem'] = facts.get('ansible_memtotal_mb')
data['cpu'] = "%s %d核" % (cpu_describe, facts.get('ansible_processor_count'))
data['disk'] = self.__filter_disk(facts.get('ansible_devices'), ['sr'])
data['interface'] = self.__gather_interface(facts, interfaces)
return {"msg": None, "data": data}
else:
return {"msg": "there result isn't ansible setup module result! can't process this data format", "data": None}
@property
def deal_setup(self):
try:
return self.__deal_setup()
except Exception as e:
return {"msg": "deal with setup data failed, %s" % e.message, "data": None}
def __deal_ping(self):
"""
处理ansible ping模块收集到的数据
:return: <dict> {"msg": <str>, "data": {"success": <bool>}}, 注意msg是异常信息, 有msg时 data为None
"""
result = self._success_data
module_name = result['invocation'].get('module_name') if result.get('invocation') else None
if module_name is not None:
if module_name != "ping":
return {"msg": "the property only for ansible setup module result!, can't support other module", "data":None}
else:
ping = True if result.get('ping') == "pong" else False
return {"msg": None, "data": {"success": ping}}
else:
return {"msg": "there isn't module_name field! can't process this data format", "data": None}
@property
def deal_ping(self):
try:
return self.__deal_ping()
except Exception as e:
return {"msg": "deal with ping data failed, %s" % e.message, "data": None}
@classmethod
def generate_fake(cls, count=20):
from random import seed, choice
import forgery_py
seed()
for i in range(count):
result = cls(name=forgery_py.name.full_name(),
success=forgery_py.lorem_ipsum.sentence(),
failed=forgery_py.lorem_ipsum.sentence(),
skipped=forgery_py.lorem_ipsum.sentence(),
unreachable=forgery_py.lorem_ipsum.sentence(),
no_host=forgery_py.lorem_ipsum.sentence(),
)
try:
result.task = choice(AnsibleTask.objects.all())
result.save()
logger.debug('Generate fake HostResult: %s' % result.name)
except Exception as e:
print('Error: %s, continue...' % e.message)
continue
# ~*~ 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
"""
# ~*~ 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
# 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
from taskers import *
\ No newline at end of file
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!')
# ~*~ 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}
......@@ -9,8 +9,5 @@ __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'),
url(r'^task/list$', page_view.TaskListView.as_view(), name='page-task-list'),
]
\ No newline at end of file
# ~*~ 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
......
......@@ -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 = TaskRecord
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'
......@@ -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('\tAsset: %s' % asset.ip)
......@@ -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>
......
......@@ -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('\tAsset: %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)
# ~*~ 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'),
......
......@@ -19,3 +19,4 @@ itsdangerous==0.24
tornado==4.4.2
eventlet==0.20.1
django-filter==1.0.0
passlib==1.7.1
......@@ -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():
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册