Commit 91587313 authored by ibuler's avatar ibuler

[Fixture] 添加runner run record

parent a822f667
...@@ -23,19 +23,16 @@ class AssetCreateForm(forms.ModelForm): ...@@ -23,19 +23,16 @@ class AssetCreateForm(forms.ModelForm):
self.instance.tags.clear() self.instance.tags.clear()
self.instance.tags.add(*tuple(tags)) self.instance.tags.add(*tuple(tags))
# def clean(self): def clean_admin_user(self):
# clean_data = super(AssetCreateForm, self).clean() if not self.cleaned_data['admin_user']:
# ip = clean_data.get('ip') raise forms.ValidationError(_('Select admin user'))
# port = clean_data.get('port') return self.cleaned_data['admin_user']
# query = Asset.objects.filter(ip=ip, port=port)
# if query:
# raise forms.ValidationError('this asset has exists.')
class Meta: class Meta:
model = Asset model = Asset
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all()) tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all())
fields = [ fields = [
'hostname', 'ip', 'port', 'type', 'comment', 'admin_user', 'system_users', 'idc', 'groups', 'hostname', 'ip', 'port', 'type', 'comment', 'admin_user', 'idc', 'groups',
'other_ip', 'remote_card_ip', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no', 'other_ip', 'remote_card_ip', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no',
'cabinet_pos', 'number', 'status', 'env', 'sn', 'tags', 'cabinet_pos', 'number', 'status', 'env', 'sn', 'tags',
] ]
...@@ -44,8 +41,6 @@ class AssetCreateForm(forms.ModelForm): ...@@ -44,8 +41,6 @@ class AssetCreateForm(forms.ModelForm):
'data-placeholder': _('Select asset groups')}), 'data-placeholder': _('Select asset groups')}),
'tags': forms.SelectMultiple(attrs={'class': 'select2', 'tags': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select asset tags')}), 'data-placeholder': _('Select asset tags')}),
'system_users': forms.SelectMultiple(attrs={'class': 'select2',
'data-placeholder': _('Select asset system users')}),
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}), 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}),
} }
help_texts = { help_texts = {
......
...@@ -94,9 +94,9 @@ class Asset(models.Model): ...@@ -94,9 +94,9 @@ class Asset(models.Model):
'ip': self.ip, 'ip': self.ip,
'port': self.port, 'port': self.port,
'groups': [group.name for group in self.groups.all()], 'groups': [group.name for group in self.groups.all()],
'username': self.admin_user.username, 'username': self.admin_user.username if self.admin_user else '',
'password': self.admin_user.password, 'password': self.admin_user.password if self.admin_user else '',
'private_key': self.admin_user.private_key, 'private_key': self.admin_user.private_key if self.admin_user else None,
} }
class Meta: class Meta:
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Asset user' %}</h3> <h3>{% trans 'Asset user' %}</h3>
{{ form.admin_user|bootstrap_horizontal }} {{ form.admin_user|bootstrap_horizontal }}
{{ form.system_users|bootstrap_horizontal }}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Asset user' %}</h3> <h3>{% trans 'Asset user' %}</h3>
{{ form.admin_user|bootstrap_horizontal }} {{ form.admin_user|bootstrap_horizontal }}
{{ form.system_users|bootstrap_horizontal }}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Hardware' %}</h3> <h3>{% trans 'Hardware' %}</h3>
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
from __future__ import unicode_literals, absolute_import from __future__ import unicode_literals, absolute_import
import logging import logging
from collections import OrderedDict
import json
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 _
...@@ -17,10 +19,14 @@ class TaskRecord(models.Model): ...@@ -17,10 +19,14 @@ class TaskRecord(models.Model):
name = models.CharField(max_length=128, blank=True, verbose_name=_('Name')) name = models.CharField(max_length=128, blank=True, verbose_name=_('Name'))
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)
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'))
assets = models.TextField(blank=True, null=True, verbose_name=_('Assets')) assets = models.TextField(blank=True, null=True, verbose_name=_('Assets for hostname')) # Asset inventory may be change
result = models.TextField(blank=True, null=True, verbose_name=_('Task result')) _modules_args = models.TextField(blank=True, null=True, verbose_name=_('Task module and args json format'))
pattern = models.CharField(max_length=64, default='all', verbose_name=_('Task run pattern'))
result = models.TextField(blank=True, null=True, verbose_name=_('Task raw result'))
summary = models.TextField(blank=True, null=True, verbose_name=_('Task summary'))
def __unicode__(self): def __unicode__(self):
return "%s" % self.uuid return "%s" % self.uuid
...@@ -29,3 +35,25 @@ class TaskRecord(models.Model): ...@@ -29,3 +35,25 @@ class TaskRecord(models.Model):
def total_assets(self): def total_assets(self):
return self.assets.split(',') return self.assets.split(',')
@property
def assets_json(self):
from assets.models import Asset
return [Asset.objects.get(hostname=hostname)._to_secret_json()
for hostname in self.total_assets
if Asset.objects.exists(hostname=hostname)]
@property
def module_args(self):
task_tuple = []
for module, args in json.loads(self._modules_args, object_pairs_hook=OrderedDict).items():
task_tuple.append((module, args))
return task_tuple
@module_args.setter
def module_args(self, task_tuple):
module_args_ = OrderedDict({})
for module, args in task_tuple:
module_args_[module] = args
self._modules_args = json.dumps(module_args_)
# coding: utf-8 # coding: utf-8
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import json
import time import time
...@@ -33,13 +34,50 @@ def asset_test_ping_check(assets): ...@@ -33,13 +34,50 @@ def asset_test_ping_check(assets):
return result['contacted'].keys(), result['dark'].keys() return result['contacted'].keys(), result['dark'].keys()
@shared_task(bind=True)
def run_AdHoc(self, task_tuple, assets,
task_name='Ansible AdHoc runner', pattern='all', record=True):
runner = AdHocRunner(assets)
if record:
from .models import TaskRecord
if not TaskRecord.objects.filter(uuid=self.request.id):
record = TaskRecord(uuid=self.request.id,
name=task_name,
assets=','.join(asset['hostname'] for asset in assets),
module_args=task_tuple,
pattern=pattern)
record.save()
else:
record = TaskRecord.objects.get(uuid=self.request.id)
record.date_start = timezone.now()
ts_start = time.time()
logger.warn('Start runner {}'.format(task_name))
result = runner.run(task_tuple, pattern=pattern, task_name=task_name)
timedelta = round(time.time() - ts_start, 2)
summary = runner.clean_result()
if record:
record.date_finished = timezone.now()
record.is_finished = True
record.result = json.dumps(result)
record.summary = json.dumps(summary)
record.timedelta = timedelta
if len(summary['failed']) == 0:
record.is_success = True
else:
record.is_success = False
record.save()
return summary
@shared_task(bind=True) @shared_task(bind=True)
def push_users(self, assets, users): def push_users(self, assets, users):
""" """
user: { user: {
username: xxx, name: 'web',
shell: /bin/bash, username: 'web',
password: 'staf', shell: '/bin/bash',
password: '123123123',
public_key: 'string', public_key: 'string',
sudo: '/bin/whoami,/sbin/ifconfig' sudo: '/bin/whoami,/sbin/ifconfig'
} }
...@@ -49,8 +87,8 @@ def push_users(self, assets, users): ...@@ -49,8 +87,8 @@ def push_users(self, assets, users):
if isinstance(assets, dict): if isinstance(assets, dict):
assets = [assets] assets = [assets]
task_tuple = [] task_tuple = []
for user in users: for user in users:
logger.debug('Push user: {}'.format(user))
# 添加用户, 设置公钥, 设置sudo # 添加用户, 设置公钥, 设置sudo
task_tuple.extend([ task_tuple.extend([
('user', 'name={} shell={} state=present password={}'.format( ('user', 'name={} shell={} state=present password={}'.format(
...@@ -65,16 +103,19 @@ def push_users(self, assets, users): ...@@ -65,16 +103,19 @@ def push_users(self, assets, users):
user['username'], user.get('sudo', '/bin/whoami') user['username'], user.get('sudo', '/bin/whoami')
)) ))
]) ])
record = TaskRecord(name='Push user', task_name = 'Push user {}'.format(','.join([user['name'] for user in users]))
record = TaskRecord(name=task_name,
uuid=self.request.id, uuid=self.request.id,
date_start=timezone.now(), date_start=timezone.now(),
assets=','.join(asset['hostname'] for asset in assets)) assets=','.join(asset['hostname'] for asset in assets))
record.save() record.save()
logger.info('Runner start {0}'.format(timezone.now())) logger.info('Runner {0} start {1}'.format(task_name, timezone.now()))
hoc = AdHocRunner(assets) hoc = AdHocRunner(assets)
ts_start = time.time()
_ = hoc.run(task_tuple) _ = hoc.run(task_tuple)
logger.info('Runner complete {0}'.format(timezone.now())) logger.info('Runner {0} complete {1}'.format(task_name, timezone.now()))
result_clean = hoc.clean_result() result_clean = hoc.clean_result()
record.time = int(time.time() - ts_start)
record.date_finished = timezone.now() record.date_finished = timezone.now()
record.is_finished = True record.is_finished = True
...@@ -82,6 +123,6 @@ def push_users(self, assets, users): ...@@ -82,6 +123,6 @@ def push_users(self, assets, users):
record.is_success = True record.is_success = True
else: else:
record.is_success = False record.is_success = False
record.result = result_clean record.result = json.dumps(result_clean)
record.save() record.save()
return result_clean return result_clean
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% 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="ibox-title">
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{# {{ form.date_expired|bootstrap_horizontal }}#}
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
{% extends 'cron/_cron.html' %}
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" >
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
This diff is collapsed.
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create cron" %} </a></div>
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="cron_list_table">
<thead>
<tr>
<th class="text-center">
{# <div><input id="" type="checkbox" class="ipt_check_all"><label></label></div>#}
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Time(minute-hour-day-month-weekday)' %}</th>
<th class="text-center">{% trans 'Job' %}</th>
<th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% include "users/_user_bulk_update_modal.html" %}
{#{% include "users/_user_import_modal.html" %}#}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#cron_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "ops:page-cron-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 2, createdCell: function (td, cellData, rowData) {
var cron_time_tmp = "{0}-{1}-{2}-{3}-{4}";
var cron_time = cron_time_tmp.format(rowData.minute, rowData.hour, rowData.day, rowData.month, rowData.weekday);
var innerHtml = '<span>' + cron_time + '</span>';
$(td).html(innerHtml.replace('99991937', rowData.id));
}},
{targets: 5, createdCell: function (td, cellData, rowData) {
var job_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-primary m-l-xs">{% trans "Job" %}</a>'.replace('99991937', cellData);
var update_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
if (rowData.id === 1 || rowData.username == "admin") {
$(td).html(update_btn)
} else {
$(td).html(job_btn + update_btn + del_btn)
}
}}],
ajax_url: '{% url "api-ops:crontable-list" %}',
columns: [{data: "id"}, {data: "name" }, {data: "month" }, {data: "job" }, {data: "user" }, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#cron_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#cron_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
function doUpdate() {
$('#user_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
}).on('click', '.btn_user_delete', function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#cron_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected user.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('click', '#btn_user_bulk_update', function(){
var json_data = $('#fm_user_bulk_update').serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.role != '') {
body.role = json_data.role;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
new_groups = body.groups.map(Number);
body.groups = new_groups;
}
var $data_table = $('#cron_list_table').DataTable()
var post_list = [];
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
$('#cron_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#cron_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script>
{% endblock %}
{% extends 'cron/_cron.html' %}
{% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block username %}
<div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
</div>
</div>
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control">
</div>
</div>
{% endblock %}
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% block content_left_head %}
<link href="{% static 'css/plugins/datepicker/datepicker3.css' %}" rel="stylesheet">
<style>
#search_btn {
margin-bottom: 0;
}
</style>
{% endblock %}
{% block table_search %}
<form id="search_form" method="get" action="" class="pull-right form-inline">
<div class="form-group" id="date">
<div class="input-daterange input-group" id="datepicker">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_from" value="{{ date_from }}">
<span class="input-group-addon">to</span>
<input type="text" class="input-sm form-control" style="width: 100px;" name="date_to" value="{{ date_to }}">
</div>
</div>
<div class="input-group">
<input type="text" class="form-control input-sm" name="keyword" placeholder="Keyword" value="{{ keyword }}">
</div>
<div class="input-group">
<div class="input-group-btn">
<button id='search_btn' type="submit" class="btn btn-sm btn-primary">
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th></th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Success' %}</th>
<th class="text-center">{% trans 'Finished' %}</th>
<th class="text-center">{% trans 'Date start' %}</th>
<th class="text-center">{% trans 'Time' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
{% endblock %}
{% block table_body %}
{% for object in task_record_list %}
<tr class="gradeX">
<td class="text-center"><input type="checkbox" class="cbx-term"> </td>
<td class="text-center"><a href="{% url 'ops:task-record-detail' pk=object.uuid %}">{{ object.name }}</a></td>
<td class="text-center">{{ object.total_assets|length }}</td>
<td class="text-center">
{% if object.is_success %}
<i class="fa fa-check text-navy"></i>
{% else %}
<i class="fa fa-times text-danger"></i>
{% endif %}
</td>
<td class="text-center">
{% if object.is_finished %}
<i class="fa fa-check text-navy"></i>
{% else %}
<i class="fa fa-times text-danger"></i>
{% endif %}
</td>
<td class="text-center">{{ object.date_start }}</td>
<td class="text-center">{{ object.timedelta }} s</td>
<td class="text-center">
<a href="" class="btn btn-xs btn-info">{% trans "Repush" %}</a>
</td>
</tr>
{% endfor %}
{% endblock %}
{# comment #}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function() {
$('table').DataTable({
"searching": false,
"paging": false,
"bInfo" : false,
"order": []
});
$('.select2').select2();
$('#date .input-daterange').datepicker({
dateFormat: 'mm/dd/yy',
keyboardNavigation: false,
forceParse: false,
autoclose: true
});
})
</script>
{# function terminateConnection(data) {#}
{# function success() {#}
{# window.setTimeout(function () {#}
{# window.location.reload()#}
{# }, 300)#}
{# }#}
{# var the_url = "{% url 'api-applications:terminate-connection' %}";#}
{# APIUpdateAttr({url: the_url, method: 'POST', body: JSON.stringify(data), success: success, success_message: 'Terminate success'});#}
{# }#}
{# $(document).ready(function() {#}
{# $('table').DataTable({#}
{# "searching": false,#}
{# "paging": false,#}
{# "bInfo" : false,#}
{# "order": []#}
{# });#}
{# $('.select2').select2();#}
{# $('#date .input-daterange').datepicker({#}
{# dateFormat: 'mm/dd/yy',#}
{# keyboardNavigation: false,#}
{# forceParse: false,#}
{# autoclose: true#}
{# });#}
{# }).on('click', '.btn-term', function () {#}
{# var $this = $(this);#}
{# var proxy_log_id = $this.attr('value');#}
{# var data = {#}
{# proxy_log_id: proxy_log_id#}
{# };#}
{# terminateConnection(data)#}
{# }).on('click', '#btn_bulk_update', function () {#}
{# var data = [];#}
{# $('.cbx-term:checked').each(function () {#}
{# data.push({proxy_log_id: $(this).attr('value')})#}
{# });#}
{# terminateConnection(data)#}
{# })#}
{# </script>#}
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% 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="ibox-title">
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{# {{ form.date_expired|bootstrap_horizontal }}#}
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" >
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
This diff is collapsed.
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "users:user-create" %}" class="btn btn-sm btn-primary"> {% trans "Create sudo" %} </a></div>
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btnbtn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="sudo_list_table">
<thead>
<tr>
<th class="text-center">
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Privileges' %}</th>
<th class="text-center">{% trans 'Extra Lines' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{#{% include "users/_user_bulk_update_modal.html" %}#}
{#{% include "users/_user_import_modal.html" %}#}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#sudo_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "ops:page-sudo-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{targets: 4, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info m-l-xs">{% trans "Update" %}</a>'.replace('99991937', cellData);
var preview_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-info m-l-xs">{% trans "Preview" %}</a>'.replace('99991937', cellData);
var job_btn = '<a href="{% url "users:user-update" pk=99991937 %}" class="btn btn-xs btn-primary m-l-xs">{% trans "Job" %}</a>'.replace('99991937', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData);
if (rowData.id === 1 || rowData.username == "admin") {
$(td).html(update_btn)
} else {
$(td).html(preview_btn + job_btn + update_btn + del_btn)
}
}}],
ajax_url: '{% url "api-ops:sudo-list" %}',
columns: [{data: "id"}, {data: "name" }, {data: "privilege_items" }, {data: "extra_lines" }, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#sudo_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
function doUpdate() {
$('#user_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
}).on('click', '.btn_user_delete', function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected user.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('click', '#btn_user_bulk_update', function(){
var json_data = $('#fm_user_bulk_update').serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.role != '') {
body.role = json_data.role;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
new_groups = body.groups.map(Number);
body.groups = new_groups;
}
var $data_table = $('#sudo_list_table').DataTable()
var post_list = [];
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#sudo_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block username %}
<div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
</div>
</div>
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control">
</div>
</div>
{% endblock %}
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load bootstrap %}
{% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% 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="ibox-title">
<h5>{% block user_template_title %}{% trans 'Create user' %}{% endblock %}</h5>
<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>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<form method="post" class="form-horizontal" action="" enctype="multipart/form-data">
{% csrf_token %}
<h3>{% trans 'Account' %}</h3>
{% block username %} {% endblock %}
{{ form.email|bootstrap_horizontal }}
{{ form.name|bootstrap_horizontal }}
{{ form.groups|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
{% block password %} {% endblock %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3>
{{ form.role|bootstrap_horizontal }}
<div class="form-group {% if form.date_expired.errors %} has-error {% endif %}" id="date_5">
<label for="{{ form.date_expired.id_for_label }}" class="col-sm-2 control-label">{{ form.date_expired.label }}</label>
<div class="col-sm-9">
<div class="input-group date">
<span class="input-group-addon"><i class="fa fa-calendar"></i></span>
<input id="{{ form.date_expired.id_for_label }}" name="{{ form.date_expired.html_name }}" type="text" class="form-control" value="{{ form.date_expired.value|date:'Y-m-d' }}">
</div>
<span class="help-block ">{{ form.date_expired.errors }}</span>
</div>
</div>
{# {{ form.date_expired|bootstrap_horizontal }}#}
<div class="form-group">
<label for="{{ form.enable_otp.id_for_label }}" class="col-sm-2 control-label">{% trans 'Enable OTP' %}</label>
<div class="col-sm-8">
{{ form.enable_otp }}
</div>
</div>
<div class="hr-line-dashed"></div>
<h3>{% trans 'Profile' %}</h3>
{{ form.phone|bootstrap_horizontal }}
{{ form.wechat|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/plugins/datapicker/bootstrap-datepicker.js' %}"></script>
<script>
$(document).ready(function () {
$('.select2').select2();
$('.input-group.date').datepicker({
format: "yyyy-mm-dd",
todayBtn: "linked",
keyboardNavigation: false,
forceParse: false,
calendarWeeks: true,
autoclose: true
});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% load bootstrap %}
{% block user_template_title %}{% trans "Create user" %}{% endblock %}
{% block username %}
{{ form.username|bootstrap_horizontal }}
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-8 controls" >
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
{% extends '_base_list.html' %}
{% load i18n static %}
{% block table_search %}
{% endblock %}
{% block table_container %}
<div class="uc pull-left m-l-5 m-r-5"><a href="{% url "ops:page-task-create" %}" class="btn btn-sm btn-primary"> {% trans "Create task" %} </a></div>
{#<div class="uc pull-left"><a href="javascript:void(0);" class="btn btnbtn-sm btn-primary" data-toggle="modal" data-target="#user_import_modal"> {% trans "Import user" %} </a></div>#}
<table class="table table-striped table-bordered table-hover " id="sudo_list_table">
<thead>
<tr>
<th class="text-center">
<input id="" type="checkbox" class="ipt_check_all">
</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'UUID' %}</th>
<th class="text-center">{% trans 'Start' %}</th>
<th class="text-center">{% trans 'Completed' %}</th>
<th class="text-center">{% trans 'Action' %}</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div id="actions" class="hide">
<div class="input-group">
<select class="form-control m-b" style="width: auto" id="slct_bulk_update">
<option value="delete">{% trans 'Delete selected' %}</option>
<option value="update">{% trans 'Update selected' %}</option>
<option value="deactive">{% trans 'Deactive selected' %}</option>
</select>
<div class="input-group-btn pull-left" style="padding-left: 5px;">
<button id='btn_bulk_update' style="height: 32px;" class="btn btn-sm btn-primary">
{% trans 'Submit' %}
</button>
</div>
</div>
</div>
{% endblock %}
{% block content_bottom_left %}{% endblock %}
{% block custom_foot_js %}
<script src="{% static 'js/jquery.form.min.js' %}"></script>
<script>
$(document).ready(function(){
var options = {
ele: $('#sudo_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "ops:page-task-detail" pk=99991937 %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('99991937', rowData.id));
}},
{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) {
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_user_delete" data-uid="99991937">{% trans "Delete" %}</a>'.replace('99991937', cellData)
$(td).html(del_btn)
}}],
ajax_url: '{% url "api-ops:task-list" %}',
columns: [{data: "name"}, {data: "uuid" }, {data: "start" }, {data: "completed" }, {data: "id" }],
op_html: $('#actions').html()
};
var table = jumpserver.initDataTable(options);
$('.buttons-pdf').click(function () {
var users = [];
var rows = table.rows('.selected').data();
$.each(rows, function (index, obj) {
users.push(obj.id)
})
});
}).on('click', '#btn_bulk_update', function(){
var action = $('#slct_bulk_update').val();
var $data_table = $('#sudo_list_table').DataTable();
var id_list = [];
var plain_id_list = [];
$data_table.rows({selected: true}).every(function(){
id_list.push({id: this.data().id});
plain_id_list.push(this.data().id);
});
if (id_list === []) {
return false;
}
var the_url = "{% url 'api-users:user-list' %}";
function doDeactive() {
var body = $.each(id_list, function(index, user_object) {
user_object['is_active'] = false;
});
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(body)});
$data_table.ajax.reload();
jumpserver.checked = false;
}
function doDelete() {
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected users !!!' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
var url_delete = the_url + '?id__in=' + JSON.stringify(plain_id_list);
APIUpdateAttr({url: url_delete, method: 'DELETE', success: success, error: fail});
jumpserver.checked = false;
});
}
function doUpdate() {
$('#user_bulk_update_modal').modal('show');
}
switch(action) {
case 'deactive':
doDeactive();
break;
case 'delete':
doDelete();
break;
case 'update':
doUpdate();
break;
default:
break;
}
}).on('click', '.btn_user_delete', function(){
var $this = $(this);
function doDelete() {
var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk=99991937 %}'.replace('99991937', uid);
var body = {};
var success = function() {
var msg = "{% trans 'User Deleted.' %}";
swal("{% trans 'User Delete' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
};
var fail = function() {
var msg = "{% trans 'User Deleting failed.' %}";
swal("{% trans 'User Delete' %}", msg, "error");
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
method: 'DELETE',
success: success,
error: fail
});
}
swal({
title: "{% trans 'Are you sure?' %}",
text: "{% trans 'This will delete the selected user.' %}",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "{% trans 'Confirm' %}",
closeOnConfirm: false
}, function() {
doDelete();
});
}).on('click', '#btn_user_bulk_update', function(){
var json_data = $('#fm_user_bulk_update').serializeObject();
var body = {};
body.enable_otp = (json_data.enable_otp === 'on')? true: false;
if (json_data.role != '') {
body.role = json_data.role;
}
if (json_data.groups != undefined) {
body.groups = json_data.groups;
}
if (typeof body.groups === 'string') {
body.groups = [parseInt(body.groups)]
} else if(typeof body.groups === 'array') {
new_groups = body.groups.map(Number);
body.groups = new_groups;
}
var $data_table = $('#sudo_list_table').DataTable()
var post_list = [];
$data_table.rows({selected: true}).every(function(){
var content = Object.assign({id: this.data().id}, body);
post_list.push(content);
});
if (post_list === []) {
return false
}
var the_url = "{% url 'api-users:user-list' %}";
var success = function() {
var msg = "{% trans 'The selected users has been updated successfully.' %}";
swal("{% trans 'User Updated' %}", msg, "success");
$('#sudo_list_table').DataTable().ajax.reload();
jumpserver.checked = false;
};
APIUpdateAttr({url: the_url, method: 'PATCH', body: JSON.stringify(post_list), success: success});
$('#user_bulk_update_modal').modal('hide');
}).on('click', '#btn_user_import', function() {
var $form = $('#fm_user_import');
$form.find('.help-block').remove();
function success (data) {
if (data.success === false) {
var $help = $form.find('.help-block');
$('<span />', {class: 'help-block text-danger'}).html(data.msg).insertAfter($('#id_excel'));
} else {
$('#user_import_modal').modal('hide');
var $data_table = $('#sudo_list_table').DataTable();
toastr.success("{% trans 'Import User Success.' %}");
$data_table.ajax.reload();
}
}
$form.ajaxSubmit({success: success});
})
</script>
{% endblock %}
{% extends 'sudo/_sudo.html' %}
{% load i18n %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block username %}
<div class="form-group">
<label for="{{ form.username.id_for_label }}" class="col-sm-2 control-label">{% trans 'Username' %}</label>
<div class="col-sm-9 controls" >
<input id="{{ form.username.id_for_label }}" name="{{ form.username.html_name }}" type="text" value="{{ user_object.username }}" readonly class="form-control">
</div>
</div>
{% endblock %}
{% block password %}
<h3>{% trans 'Password' %}</h3>
<div class="form-group">
<label for="password" class="col-sm-2 control-label">{% trans 'Password' %}</label>
<div class="col-sm-9 controls" >
<input id="password" name="password" type="password" class="form-control">
</div>
</div>
{% endblock %}
...@@ -3,11 +3,12 @@ from __future__ import unicode_literals ...@@ -3,11 +3,12 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from ops import views as page_view from .. import views
__all__ = ["urlpatterns"] __all__ = ["urlpatterns"]
urlpatterns = [ urlpatterns = [
# TResource Task url # TResource Task url
url(r'^task/list$', page_view.TaskListView.as_view(), name='page-task-list'), url(r'^task-record/$', views.TaskRecordListView.as_view(), name='task-record-list'),
url(r'^task-record/(?P<pk>[0-9a-zA-Z-]+)/$', views.TaskRecordDetailView.as_view(), name='task-record-detail'),
] ]
\ No newline at end of file
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from collections import defaultdict
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
class CommandResultCallback(CallbackBase):
def __init__(self, display=None):
self.result_q = dict(contacted={}, dark={})
super(CommandResultCallback, self).__init__(display)
def gather_result(self, n, res):
self.result_q[n][res._host.name] = {}
self.result_q[n][res._host.name]['cmd'] = res._result.get('cmd')
self.result_q[n][res._host.name]['stderr'] = res._result.get('stderr')
self.result_q[n][res._host.name]['stdout'] = res._result.get('stdout')
self.result_q[n][res._host.name]['rc'] = res._result.get('rc')
def v2_runner_on_ok(self, result):
self.gather_result("contacted", result)
def v2_runner_on_failed(self, result, ignore_errors=False):
self.gather_result("dark", result)
def v2_runner_on_unreachable(self, result):
self.gather_result("dark", result)
def v2_runner_on_skipped(self, result):
self.gather_result("dark", result)
class AdHocResultCallback(CallbackBase): class AdHocResultCallback(CallbackBase):
""" """
Custom Callback AdHoc result Callback
""" """
def __init__(self, display=None): def __init__(self, display=None):
self.result_q = dict(contacted={}, dark={}) self.result_q = dict(contacted={}, dark={})
......
...@@ -14,7 +14,8 @@ from ansible.utils.vars import load_extra_vars ...@@ -14,7 +14,8 @@ from ansible.utils.vars import load_extra_vars
from ansible.utils.vars import load_options_vars from ansible.utils.vars import load_options_vars
from .inventory import JMSInventory from .inventory import JMSInventory
from .callback import AdHocResultCallback, PlaybookResultCallBack from .callback import AdHocResultCallback, PlaybookResultCallBack, \
CommandResultCallback
from common.utils import get_logger from common.utils import get_logger
...@@ -29,6 +30,7 @@ class AnsibleError(StandardError): ...@@ -29,6 +30,7 @@ class AnsibleError(StandardError):
pass pass
# Jumpserver not use playbook
class PlayBookRunner(object): class PlayBookRunner(object):
""" """
用于执行AnsiblePlaybook的接口.简化Playbook对象的使用. 用于执行AnsiblePlaybook的接口.简化Playbook对象的使用.
...@@ -136,6 +138,8 @@ class AdHocRunner(object): ...@@ -136,6 +138,8 @@ class AdHocRunner(object):
] ]
) )
results_callback_class = AdHocResultCallback
def __init__(self, def __init__(self,
hosts=C.DEFAULT_HOST_LIST, hosts=C.DEFAULT_HOST_LIST,
forks=C.DEFAULT_FORKS, # 5 forks=C.DEFAULT_FORKS, # 5
...@@ -156,7 +160,7 @@ class AdHocRunner(object): ...@@ -156,7 +160,7 @@ class AdHocRunner(object):
self.variable_manager = VariableManager() self.variable_manager = VariableManager()
self.loader = DataLoader() self.loader = DataLoader()
self.gather_facts = gather_facts self.gather_facts = gather_facts
self.results_callback = AdHocResultCallback() self.results_callback = AdHocRunner.results_callback_class()
self.options = self.Options( self.options = self.Options(
connection=connection_type, connection=connection_type,
timeout=timeout, timeout=timeout,
...@@ -171,7 +175,8 @@ class AdHocRunner(object): ...@@ -171,7 +175,8 @@ class AdHocRunner(object):
private_key_file=private_key_file, private_key_file=private_key_file,
) )
self.variable_manager.extra_vars = load_extra_vars(self.loader, options=self.options) self.variable_manager.extra_vars = load_extra_vars(self.loader,
options=self.options)
self.variable_manager.options_vars = load_options_vars(self.options) self.variable_manager.options_vars = load_options_vars(self.options)
self.passwords = passwords or {} self.passwords = passwords or {}
self.inventory = JMSInventory(hosts) self.inventory = JMSInventory(hosts)
...@@ -252,7 +257,7 @@ class AdHocRunner(object): ...@@ -252,7 +257,7 @@ class AdHocRunner(object):
""" """
:return: { :return: {
"success": ['hostname',], "success": ['hostname',],
"failed": [{'hostname': 'msg'}, {}], "failed": [('hostname', 'msg'), {}],
} }
""" """
result = {'success': [], 'failed': []} result = {'success': [], 'failed': []}
...@@ -262,26 +267,30 @@ class AdHocRunner(object): ...@@ -262,26 +267,30 @@ class AdHocRunner(object):
for host, msgs in self.results_callback.result_q['dark'].items(): for host, msgs in self.results_callback.result_q['dark'].items():
msg = '\n'.join(['{}: {}'.format(msg.get('invocation', {}).get('module_name'), msg = '\n'.join(['{}: {}'.format(msg.get('invocation', {}).get('module_name'),
msg.get('msg', '')) for msg in msgs]) msg.get('msg', '')) for msg in msgs])
result['failed'].append({host: msg}) result['failed'].append((host, msg))
return result return result
def test_run(): def test_run():
assets = [ assets = [
{ {
"hostname": "192.168.152.129", "hostname": "192.168.244.129",
"ip": "192.168.152.129", "ip": "192.168.244.129",
"port": 22, "port": 22,
"username": "root", "username": "root",
"password": "redhat", "password": "redhat",
}, },
] ]
task_tuple = (('shell', 'ls'), ('ping', '')) task_tuple = (('shell', 'ls'),)
hoc = AdHocRunner(hosts=assets) hoc = AdHocRunner(hosts=assets)
hoc.results_callback = CommandResultCallback()
ret = hoc.run(task_tuple) ret = hoc.run(task_tuple)
print(ret) print(ret)
play = PlayBookRunner(assets, playbook_path='/tmp/some.yml') #play = PlayBookRunner(assets, playbook_path='/tmp/some.yml')
""" """
# /tmp/some.yml # /tmp/some.yml
--- ---
...@@ -293,7 +302,7 @@ def test_run(): ...@@ -293,7 +302,7 @@ def test_run():
- name: exec uptime - name: exec uptime
shell: uptime shell: uptime
""" """
play.run() #play.run()
if __name__ == "__main__": if __name__ == "__main__":
......
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals from __future__ import unicode_literals
import json
from django.conf import settings from django.conf import settings
from django.views.generic.list import ListView from django.views.generic import ListView, DetailView
from users.utils import AdminUserRequiredMixin
from .models import TaskRecord from .models import TaskRecord
class TaskListView(AdminUserRequiredMixin, ListView): class TaskRecordListView(ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = TaskRecord model = TaskRecord
context_object_name = 'tasks' ordering = ('-date_start',)
template_name = 'task/list.html' context_object_name = 'task_record_list'
template_name = 'ops/task_record_list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'task': 'Assets', 'app': 'Ops',
'action': 'Create asset', 'action': 'Task record list',
} }
kwargs.update(context) kwargs.update(context)
return super(TaskListView, self).get_context_data(**kwargs) return super(TaskRecordListView, self).get_context_data(**kwargs)
class TaskRecordDetailView(DetailView):
model = TaskRecord
template_name = 'ops/task_record_detail.html'
def get_context_data(self, **kwargs):
context = {
'app': 'Ops',
'action': 'Task record detail',
'results': json.loads(self.object.summary),
}
kwargs.update(context)
return super(TaskRecordDetailView, self).get_context_data(**kwargs)
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,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:page-task-list' %}">{% trans 'Task' %}</a></li> <li id="task"><a href="{% url 'ops:task-record-list' %}">{% trans 'Task Record' %}</a></li>
</ul> </ul>
</li> </li>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment