python manage.py migrate
python manage.py startapp task
获取用户名
#add redis for celery
redis==2.10.6
djangorestframework==3.11.0
Jinja2==2.11.2
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, APP_CODE),
}
}
login_user_data = client.bk_login.get_user({
"bk_token": request.COOKIES.get("bk_token")
})
username = request.user.username
if login_user_data['result']:
username = login_user_data['data']['bk_username']
sys_time = datetime.datetime.now()
sys_time_str = sys_time.strftime('%Y-%m-%d %H:%M:%S')
# python3 b64encode 需要转码
data = client.job.fast_execute_script({
"bk_biz_id": bk_biz_id,
"task_name": task_name,
"account": account,
"script_content": str(base64.b64encode(script_content.encode('utf-8')), "utf-8"),
"script_param": str(base64.b64encode(script_param.encode('utf-8')), "utf-8"),
"script_type": script_type,
"ip_list": ip_list,
})
####
job_detail = client.job.get_job_detail({
'bk_biz_id': int(biz_id),
'bk_job_id': int(job_id)
})
steps = []
job_detail_steps = job_detail['data']['steps']
i = 1
for step in job_detail_steps:
steps.append({"step_id": step['step_id'],
"script_param": str(base64.b64encode(key_word.encode('utf-8')), encoding='utf-8'),
"ip_list": [{"bk_cloud_id": 0, "ip": host_ip}]})
job_log = client.job.execute_job({
'bk_biz_id': int(biz_id),
'bk_job_id': int(job_id),
'steps': steps # job_detail['data']['steps']
})
job_instance_id = job_log['data']['job_instance_id']
###
log_info = client.job.get_job_instance_log({
"bk_biz_id": bk_biz_id,
"job_instance_id": job_instance_id
})
while not log_info['data'][0]["is_finished"]:
# sleep(1)
log_info = log_info = client.job.get_job_instance_log({
"bk_biz_id": bk_biz_id,
"job_instance_id": job_instance_id
})
if log_info['data'][0]["is_finished"]:
return log_info
return log_info
python celery note
@periodic_task(run_every=crontab(minute='*/1', hour='*', day_of_week="*", day_of_month='*', month_of_year='*'))
def task(**kwargs):
pass
## 延迟执行
@task()
def start_task(client, to_user, key):
pass
### call start task
start_task.apply_async(args=[client, to_user, key], eta=now + datetime.timedelta(seconds=10))
## create celery tasks with interval
interval_schedule = {
"every": 5,
"period": 'seconds'
}
kwargs = dict(
biz_id=biz_id,
job_instance_id=job_instance_id,
)
interval = IntervalSchedule.objects.create(**interval_schedule)
interval.save()
now = datetime.datetime.now()
task = PeriodicTask.objects.create(name='get_log_{}'.format(job_instance_id),
kwargs=json.dumps(kwargs),
interval=interval,
enabled=True,
expires=now + datetime.timedelta(minutes=1),
task="task.celery_tasks.get_instance_log")
django paginator 以及 serializers 使用
qs = ScriptLog.objects.filter()
page_num = request.GET.get('page')
page_size = request.GET.get('page_size')
paginator = Paginator(qs, page_size) # 每页25条
page = paginator.page(page_num)
data = []
obj_list = json.loads(serializers.serialize('json', page.object_list))
for obj in obj_list:
data.append(obj.get('fields'))
vue.config.js
var env = process.env.NODE_ENV
var isProduction = env === 'production'
let publicPath = '/'
if (isProduction) {
publicPath = './static/build'
}
module.exports = {
lintOnSave: 'error',
publicPath: publicPath,
indexPath: '../../templates/index.html',
devServer: {
proxy: 'http://127.0.0.1:8000',
disableHostCheck: true,
port: 8080
}
}
pacakge.json ../static/build
main.js
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from 'axios'
const http = axios.create({
baseURL: process.env.NODE_ENV === 'production' ? window.site_url : '/',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
xsrfCookieName: 'csrftoken',
xsrfHeaderName: 'X-CSRFToken',
withCredentials: true
})
Vue.prototype.$http = http
Vue.use(ElementUI);
index.html
<script>
window.site_url = '{{ SITE_URL }}'
window.username = '{{ USERNAME }}'
</script>
menu nav
<div id="nav">
<div class="logo-wrap">
<img src="./assets/logo.png" alt="">
</div>
<div class="menu-wrap">
<div class="menu-item">
<router-link to="/">进程查询</router-link>
</div>
<div class="menu-item">
<router-link to="/detail">查看详情</router-link>
</div>
</div>
<div class="username-wrap">
{{ username }}
</div>
</div>
json
{
"module": "demo",
"name": "snippet",
"search_forms": [
{"prop": "title", "label": "标题", "type": "input", "rules": [
{"required": "true", "message": "请输入标题", "trigger": "blur"}
]},
],
"forms": [
{"prop": "title", "label": "标题", "type": "input", "rules": [
{"required": "true", "message": "请输入标题", "trigger": "blur"}
]},
],
"columns": [
{"prop": "title", "label": "标题"}
]
}
data_list
<template>
<div class="page-wrap">
<div class="search-container">
<el-form ref="searchForm" :rules="searchFormRule" :model="searchForm" label-position="left" label-width="180px">
{% for formItem in searchForms %}
<el-form-item label="{{ formItem.label }}:"
prop="{{ formItem.prop }}">
{% if formItem.type == 'select' %}
<el-select v-model="searchForm.{{ formItem.prop }}" placeholder="请选择{{ formItem.label }}">
<el-option value="option1" label="选项1"></el-option>
</el-select>
{% endif %}
{% if formItem.type == 'input' %}
<el-input v-model="searchForm.{{ formItem.prop }}" placeholder="请输入{{ formItem.label }}"></el-input>
{% endif %}
{% if formItem.type == 'textarea' %}
<el-input type="textarea" :rows="3" v-model="searchForm.{{ formItem.prop }}" placeholder="请输入{{ formItem.label }}"></el-input>
{% endif %}
{% if formItem.type == 'datetime' %}
<el-date-picker type="datetime"
placeholder="选择日期"
value-format="timestamp"
v-model="searchForm.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'datetimerange' %}
<el-date-picker type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="timestamp"
v-model="searchForm.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'date' %}
<el-date-picker type="date"
placeholder="选择日期"
value-format="timestamp"
v-model="searchForm.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'daterange' %}
<el-date-picker type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="timestamp"
v-model="searchForm.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'time' %}
<el-time-picker placeholder="选择时间" v-model="searchForm.{{ formItem.prop }}"></el-time-picker>
{% endif %}
{% if formItem.type == 'timerange' %}
<el-time-picker is-range
arrow-control
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围"
v-model="searchForm.{{ formItem.prop }}"></el-time-picker>
{% endif %}
{% if formItem.type == 'switch' %}
<el-switch v-model="searchForm.{{ formItem.prop }}"></el-switch>
{% endif %}
</el-form-item>
{% endfor %}
<el-form-item>
<el-button type="primary" :disabled="loadingData" @click="onSearchSubmit('searchForm')">提交</el-button>
<el-button type="primary" @click="showCreate">创建</el-button>
</el-form-item>
</el-form>
</div>
<div class="result-container" v-loading="loadingData">
<div class="result-title">列表</div>
<el-table
:data="{{ name }}Data"
style="width: 100%"
@selection-change="handleSelectionChange">
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
type="index"
:index="tableIndex"
label="序号"
width="55">
</el-table-column>
{% for col in tableColumns %}
<el-table-column
prop="{{col.prop}}"
label="{{col.label}}">
<template slot-scope="scope">
<span>
{{ '{{' }}scope.row.{{col.prop}} || '--'{{ '}}' }}
</span>
</template>
</el-table-column>
{% endfor %}
<el-table-column
fixed="right"
label="操作"
width="120">
<template slot-scope="scope">
<el-button
@click.native.prevent="edit{{name|capitalize}}(scope.row)"
type="text"
size="small">
编辑
</el-button>
<el-button
@click.native.prevent="delete{{name|capitalize}}(scope.row)"
type="text"
size="small">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<div class="page-container">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageInfo.pageNum"
:page-sizes="[10, 20, 30]"
:page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="pageInfo.total">
</el-pagination>
</div>
</div>
<edit-{{name}} :show="dialog" :pk="pk" @on-updated="updated"></edit-{{name}}>
</div>
</template>
<script>
import Edit{{name|capitalize}} from "./components/Edit{{name|capitalize}}";
export default {
name: '{{name}}_page',
components: {
Edit{{name|capitalize}}
},
data() {
return {
saving: false,
dialog: false,
pk: null,
multipleSelection: [],
pageInfo: {
pageSize: 10,
pageNum: 1,
total: 0
},
{{ name }}Data: [],
loadingData: false,
searchForm: {
{% for form in searchForms %}
{{ form.prop }} : undefined,
{% endfor %}
},
searchFormRule: {
{% for form in searchForms %}
{{ form.prop }} : {{ form.rules }},
{% endfor %}
}
}
},
created() {
this.initData()
},
methods: {
updated() {
this.dialog = false
this.getSnippetData()
},
showCreate() {
this.dialog = true
},
edit{{name|capitalize}}(item) {
this.dialog = true
this.pk = item.id
},
tableIndex(index) {
return (this.pageInfo.pageNum -1) * this.pageInfo.pageSize + index +1
},
handleSelectionChange(val) {
this.multipleSelection = val;
console.log('>> multipleSelection: ' + val)
},
delete{{name|capitalize}}(item){
this.$confirm('此操作将永久删除该项, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
const resp = await this.$http.delete(`{{ module }}/{{name}}/${item.id}`)
if(resp.data.result) {
this.get{{name|capitalize}}Data()
this.$message({
type:'success',
message: '删除成功'
})
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
async initData() {
this.get{{ name | capitalize }}Data()
},
async get{{ name | capitalize }}Data() {
this.loadingData = true
const params = {
page: this.pageInfo.pageNum,
page_size: this.pageInfo.pageSize,
{% for form in searchForms %}
{{ form.prop }} : this.searchForm.{{ form.prop }},
{% endfor %}
}
const resp = await this.$http.get('{{ module }}/{{name}}', {params})
this.loadingData = false
if (resp.data.result) {
this.pageInfo.total = resp.data.count
this.{{ name }}Data = resp.data.data
}
},
handleSizeChange (val) {
this.pageInfo.pageSize = val
this.pageInfo.pageNum = 1
this.get{{ name | capitalize }}Data()
},
handleCurrentChange (val) {
this.pageInfo.pageNum = val
this.get{{ name | capitalize }}Data()
},
onSearchSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.get{{ name | capitalize }}Data()
} else {
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style lang="scss">
.page-wrap {
.result-title {
background: #e7e7e7;
height: 48px;
line-height: 48px;
padding: 0 16px;
text-align: left;
font-size: 16px;
}
.page-container{
padding-top: 8px;
}
}
</style>
edit
<template>
<div class="component-wrap">
<el-drawer
:with-header="false"
:before-close="handleClose"
:show-close="false"
:visible.sync="show"
size="46%"
direction="rtl"
custom-class="demo-drawer"
ref="drawer"
>
<div class="demo-drawer__title">
编辑{{name|capitalize}}
</div>
<div class="demo-drawer__content">
<el-form ref="snippetForm" :rules="{{name}}FormRule" :model="{{name}}Form" label-position="left" label-width="120px">
{% for formItem in forms %}
<el-form-item label="{{ formItem.label }}:"
prop="{{ formItem.prop }}">
{% if formItem.type == 'select' %}
<el-select v-model="{{name}}Form.{{ formItem.prop }}" placeholder="请选择{{ formItem.label }}">
<el-option value="option1" label="选项1"></el-option>
</el-select>
{% endif %}
{% if formItem.type == 'input' %}
<el-input v-model="{{name}}Form.{{ formItem.prop }}" placeholder="请输入{{ formItem.label }}"></el-input>
{% endif %}
{% if formItem.type == 'textarea' %}
<el-input type="textarea" :rows="3" v-model="{{name}}Form.{{ formItem.prop }}" placeholder="请输入{{ formItem.label }}"></el-input>
{% endif %}
{% if formItem.type == 'datetime' %}
<el-date-picker type="datetime"
placeholder="选择日期"
value-format="timestamp"
v-model="{{name}}Form.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'datetimerange' %}
<el-date-picker type="datetimerange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="timestamp"
v-model="{{name}}Form.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'date' %}
<el-date-picker type="date"
placeholder="选择日期"
value-format="timestamp"
v-model="{{name}}Form.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'daterange' %}
<el-date-picker type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="timestamp"
v-model="{{name}}Form.{{ formItem.prop }}"></el-date-picker>
{% endif %}
{% if formItem.type == 'time' %}
<el-time-picker placeholder="选择时间" v-model="{{name}}Form.{{ formItem.prop }}"></el-time-picker>
{% endif %}
{% if formItem.type == 'timerange' %}
<el-time-picker is-range
arrow-control
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围"
v-model="{{name}}Form.{{ formItem.prop }}"></el-time-picker>
{% endif %}
{% if formItem.type == 'switch' %}
<el-switch v-model="{{name}}Form.{{ formItem.prop }}"></el-switch>
{% endif %}
</el-form-item>
{% endfor %}
</el-form>
<div class="demo-drawer__footer">
<el-button type="primary" @click="$refs.drawer.closeDrawer()" :loading="saving">
<span v-if="saving">提交中...</span>
<span v-else>确定</span>
</el-button>
<el-button @click="cancelForm">取 消</el-button>
</div>
</div>
</el-drawer>
</div>
</template>
<script>
export default {
name: "Edit{{name|capitalize}}",
props: {
show: {
type: Boolean,
default: false
},
pk: {
default: null
}
},
data() {
return {
saving: false,
{{name}}Form: {
{% for form in forms %}
{{ form.prop }} : undefined,
{% endfor %}
},
{{name}}FormRule: {
{% for form in forms %}
{{ form.prop }} : {{ form.rules }},
{% endfor %}
}
}
},
methods: {
handleClose() {
if (this.saving) {
return;
}
this.$refs['{{name}}Form'].validate((valid) => {
if (valid) {
this.$confirm('确定要提交表单吗?').then(async () => {
const resp = await this.$http.post('{{module}}/{{name}}/', this.{{name}}Form)
if (resp.data.result) {
this.saving = true;
this.$message({
type: 'success',
message: `操作成功`
})
this.$emit('on-updated')
}
})
} else {
console.log('error submit!!');
return false;
}
});
},
cancelForm() {
this.saving = false;
this.$emit('on-updated')
},
}
}
</script>
<style scoped>
</style>
viewset
# -*- coding: utf-8 -*-
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from blueapps.utils.logger import logger
from config.default import PROJECT_CONFIG
from {{ app_name }}.models import {{ name }}
from {{ app_name }}.serializers import {{ name }}Serializer
class {{ name }}ViewSet(viewsets.ModelViewSet):
"""
此视图自动提供`list`,`create`,`retrieve`,`update`和`destroy`操作。
另外我们还提供了一个额外的`execute`操作。
"""
# queryset = {{ name }}.objects.all()
serializer_class = {{ name }}Serializer
@action(methods=['get'], detail=False)
def execute(self, request, *args, **kwargs):
logger.info('>>> execute start ')
result = {"result": True}
return Response(result)
def list(self, request, *args, **kwargs):
logger.info('>>>> {{ name }}ViewSet list')
page_queryset = {{ name }}.objects.filter()
page_size = PROJECT_CONFIG['PAGE_SIZE']
if request.query_params.get("page_size"):
page_size = int(request.query_params.get("page_size"))
# 实例化产生一个加密分页对象
pagination = PageNumberPagination()
pagination.page_size = page_size # 每页显示的个数
pagination.max_page_size = 50 # 每页最多显示多少条
page_list = pagination.paginate_queryset(page_queryset, request)
ser = {{ name }}Serializer(instance=page_list, many=True)
result = {"result": True,
'count': pagination.page.paginator.count,
'data': ser.data}
return Response(result)
def create(self, request, *args, **kwargs):
logger.info('>>>> {{ name }}ViewSet create')
ser = {{ name }}Serializer(data=request.data)
if ser.is_valid():
ser.save()
result = {"result": True, 'data': ser.data}
return Response(result)
result = {"result": False}
return Response(result)
def retrieve(self, request, pk=None, **kwargs):
logger.info('>>> {{ name }}ViewSet retrieve by pk = {} '.format(pk))
obj ={{ name }}.objects.get(pk=pk)
ser ={{ name }}Serializer(instance=obj)
result = {"result": True, 'data': ser.data}
return Response(result)
def update(self, request, pk=None, **kwargs):
logger.info('>>>> update')
result = {"result": True}
obj ={{ name }}.objects.get(pk=pk)
ser ={{ name }}Serializer(instance=obj)
ser.is_valid(raise_exception=True)
ser.save()
return Response(result)
def destroy(self, request, pk=None, **kwargs):
logger.info('>>>> {{ name }}ViewSet update pk {pk}'.format(pk=pk))
obj ={{ name }}.objects.get(pk=pk)
ser ={{ name }}Serializer(instance=obj)
obj.delete()
result = {"result": True, 'data': ser.data}
return Response(result)
ggg templates
# -*- coding: utf-8 -*-
import json
import os
import re
from jinja2 import Environment, PackageLoader
from config import BASE_DIR
def get_jinjia_env():
env = Environment(loader=PackageLoader('common.generator'))
return env
def get_json_obj(config_file):
with open('./config/{json_file}'.format(json_file=config_file), 'r', encoding='utf-8') as f:
json_data = f.read()
json_obj = json.loads(json_data)
return json_obj
def gen_vue_file(config_file, override=False):
json_obj = get_json_obj(config_file)
template = get_jinjia_env().get_template('data_list.j2')
template_form = get_jinjia_env().get_template('edit_form.j2')
gen_vue_comp(json_obj, override=override, template=template)
gen_vue_comp(json_obj, override=override, template=template_form, is_edit=True)
def gen_vue_comp(json_obj, override, template, is_edit=False):
searchForms = json_obj['search_forms']
forms = json_obj['forms']
tableColumns = json_obj['columns']
module = json_obj['module']
name = json_obj['name']
content = template.render(module=module,
name=name,
searchForms=searchForms,
forms=forms,
tableColumns=tableColumns)
blank_line_re = re.compile('\\s+\\n')
content = blank_line_re.sub('\n', content)
folder = os.path.join(BASE_DIR, 'vue_app') + '/src/views/{module}'.format(module=module)
if is_edit:
folder = os.path.join(BASE_DIR, 'vue_app') + '/src/views/{module}/components'.format(module=module)
if not os.path.exists(folder):
os.makedirs(name=folder)
file_path = '{folder}/{name}.vue'.format(folder=folder, name=name)
if is_edit:
file_path = '{folder}/Edit{name}.vue'.format(folder=folder, name=name.capitalize())
if override:
with open(file_path, 'w', encoding='utf-8') as fp:
fp.write(content)
print('>>> gen vue success!!!')
else:
print('>>> file already exists, you can pass extra arg: override = True, to override exists file...')
def gen_view_set(app_name, name, override=False):
template = get_jinjia_env().get_template('SampleViewSet.j2')
content = template.render(app_name=app_name, name=name)
folder = os.path.join(BASE_DIR, app_name) + '/views'
file_path = '{folder}/{name}ViewSet.py'.format(folder=folder, name=name)
if not os.path.exists(folder):
os.makedirs(name=folder)
if override:
with open(file_path, 'w', encoding='utf-8') as fp:
fp.write(content)
print('>>> gen view set success!!!')
else:
print('>>> file already exists, you can pass extra arg: override = True, to override exists file...')
urls
# -*- coding: utf-8 -*-
from django.conf.urls import url
from django.urls import include
from rest_framework.routers import DefaultRouter
from .views import views
from .views.BogViewSet import BLogViewSet
router = DefaultRouter()
router.register(r'blog', BLogViewSet, basename='blog')
urlpatterns = (
url(r'^$', views.home),
url(r'^home/', include(router.urls)),
url(r'^api/test/$', views.api_test),
)