最近在使用iview-admin开发prometheus和alertmanager管理平台。prometheus管理规则我想可以通过下载当前页面规则到本地为excel文件,然后添加若干规则后上传更新。
1.下载规则为Excel文件
<template>
<div>
<Card>
<Button icon="md-download" :loading="exportLoading" @click="exportExcel">导出规则</Button>
<tables ref="tables" v-model="tableData" :columns="columns"/>
</Card>
</div>
</template>
<script>
import Tables from '_c/tables'
import {
getPromRule
} from '@/api/data'
import excel from '@/libs/excel'
export default {
name: 'rules',
components: {
Tables
},
data () {
return {
tableData: [],
columns: [
{ title: '编号', align: 'center', key: 'id', sortable: true },
{ title: '告警名称', align: 'center', key: 'alert_name' },
{
title: '告警等级',
align: 'center',
key: 'severity',
render: (h, params) => {
if (params.row.severity === 'warning') {
return h('div', '警告')
} else if (params.row.severity === 'critical') {
return h('div', '危急')
} else if (params.row.severity === 'emergency') {
return h('div', '紧急')
}
}
},
{ title: '表达式', key: 'expr' },
{ title: '持续时长', align: 'center', key: 'duration' },
{ title: '标题', align: 'center', key: 'summary' },
{ title: '描述', align: 'center', key: 'description' },
{ title: '数据源', align: 'center', key: 'datasource' },
{ title: '告警策略', align: 'center', key: 'group' }
],
exportLoading: false
}
},
methods: {
initRulesData () {
this.filterPromRule()
},
filterPromRule () {
getPromRule().then(resp => {
if (resp.data.code === 0) {
this.tableData = resp.data.data.prom_rules
} else {
this.$Message.error('获取prometheus规则失败!')
}
})
},
exportExcel () {
if (this.tableData.length) {
this.exportLoading = true
let colTitle = []
let colKey = []
for (let col of this.columns) {
if (col.title === '操作') {
continue
}
colTitle.push(col.title)
colKey.push(col.key)
}
const params = {
title: colTitle,
key: colKey,
data: this.tableData,
autoWith: true,
filename: 'prometheus规则列表'
}
excel.export_array_to_excel(params)
this.exportLoading = false
} else {
this.$Message.error('表格数据不能为空')
}
}
},
mounted () {
this.initRulesData()
}
}
</script>
<style>
</style>
2.上传EXCEL规则文件
上传文件的内容需要把数据放在数据库中并更新prometheus配置。
2.1 前台代码
<template>
<div>
<Card>
<Upload action="" :before-upload="handleBeforeUpload" accept=".xls, .xlsx">
<Button icon="ios-cloud-upload-outline" :loading="uploadLoading" @click="handleUploadFile">上传规则</Button>
</Upload>
<tables ref="tables" v-model="tableData" :columns="columns"/>
</Card>
</div>
</template>
<script>
import Tables from '_c/tables'
import {
uploadPromRule,
getPromRule
} from '@/api/data'
import excel from '@/libs/excel'
export default {
name: 'rules',
components: {
Tables
},
data () {
return {
tableData: [],
columns: [
{ title: '编号', align: 'center', key: 'id', sortable: true },
{ title: '告警名称', align: 'center', key: 'alert_name' },
{
title: '告警等级',
align: 'center',
key: 'severity',
render: (h, params) => {
if (params.row.severity === 'warning') {
return h('div', '警告')
} else if (params.row.severity === 'critical') {
return h('div', '危急')
} else if (params.row.severity === 'emergency') {
return h('div', '紧急')
}
}
},
{ title: '表达式', key: 'expr' },
{ title: '持续时长', align: 'center', key: 'duration' },
{ title: '标题', align: 'center', key: 'summary' },
{ title: '描述', align: 'center', key: 'description' },
{ title: '数据源', align: 'center', key: 'datasource' },
{ title: '告警策略', align: 'center', key: 'group' }
],
uploadLoading: false
}
},
methods: {
initRulesData () {
this.filterPromRule()
},
filterPromRule () {
getPromRule().then(resp => {
if (resp.data.code === 0) {
this.tableData = resp.data.data.prom_rules
} else {
this.$Message.error('获取prometheus规则失败!')
}
})
},
handleBeforeUpload (file) {
const fileExt = file.name.split('.').pop().toLocaleLowerCase()
if (fileExt === 'xlsx' || fileExt === 'xls') {
this.readFile(file)
this.file = file
} else {
this.$Notice.warning({
title: '文件类型错误',
desc: '文件:' + file.name + '不是EXCEL文件,请选择后缀为.xlsx或者.xls的EXCEL文件。'
})
}
return false
},
initUpload () {
this.file = null
},
handleUploadFile () {
this.initUpload()
},
readFile (file) {
const reader = new FileReader()
reader.readAsArrayBuffer(file)
reader.onloadstart = e => {
this.uploadLoading = true
this.tableLoading = true
this.showProgress = true
}
reader.onerror = e => {
this.$Message.error('文件读取出错')
}
reader.onload = e => {
this.$Message.info('文件读取成功')
const data = e.target.result
const { header, results } = excel.read(data, 'array')
// eslint-disable-next-line no-unused-vars
const tableTitle = header.map(item => { return { title: item, key: item } })
uploadPromRule(results).then(resp => {
if (resp.data.code === 0) {
this.initRulesData()
} else {
this.$Message.error(resp.data.msg)
}
})
this.uploadLoading = false
this.tableLoading = false
}
}
},
mounted () {
this.initRulesData()
}
}
</script>
<style>
</style>
2.2 axios路由
// /src/api/data.js
export const uploadPromRule = (data) => {
return axios.request({
url: '/upload/prometheusconfigs',
method: 'post',
data
})
}
2.3 后台路由
from .v1 import *
from flask_restful import Api
api = Api(prefix="/api/v1")
api.add_resource(PrometheusUploadView, "/upload/prometheusconfigs")
2.4后台业务逻辑代码
class PrometheusUploadView(RequestPrometheusBase):
model = PromRulerModel
def post(self):
prom_rule_list = request.get_json(force=True)
failed_title = ''
if isinstance(prom_rule_list, list) and len(prom_rule_list) > 0:
add_num = 0
for rule in prom_rule_list:
if "编号" in rule:
continue
add_num += 1
prom_rule_model = PromRulerModel()
prom_rule_model.alert_name = rule["告警名称"]
prom_rule_model.expr = rule["表达式"]
prom_rule_model.duration = rule["持续时长"]
prom_rule_model.severity = rule["告警等级"]
prom_rule_model.summary = rule["标题"]
prom_rule_model.description = rule["描述"]
prom_rule_model.datasource = DataSourceModel.objects_().get_one(
DataSourceModel.name == rule["数据源"]).id
prom_rule_model.group = GroupModel.objects_().get_one(GroupModel.name == rule["告警策略"]).id
add_res = prom_rule_model.objects.add()
if not add_res:
logger.error(f"Failed add prom rule when uploading!, Error: {rule['标题']} ")
failed_title = failed_title + ',' + rule['标题']
if not failed_title:
msg = "success"
else:
msg = f"上传数据失败!失败的标题有: {failed_title}"
logger.error(msg)
if add_num > 0:
reload_res = self._reload_prom_config()
reload_res['msg'] += msg
return reload_res
else:
code = HttpCode.ERROR
msg = "上传数据为空或者数据格式有问题!"
logger.error(msg)
return pretty_result(code=code, msg=msg)