mirror of https://github.com/veops/cmdb.git
relation view [done]
This commit is contained in:
parent
1696ecf49d
commit
85222443c0
|
@ -231,9 +231,11 @@ class CIManager(object):
|
|||
item.delete()
|
||||
|
||||
for item in CIRelation.get_by(first_ci_id=ci_id, to_dict=False):
|
||||
ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
|
||||
item.delete()
|
||||
|
||||
for item in CIRelation.get_by(second_ci_id=ci_id, to_dict=False):
|
||||
ci_relation_delete.apply_async(args=(item.first_ci_id, item.second_ci_id), queue=CMDB_QUEUE)
|
||||
item.delete()
|
||||
|
||||
ci.delete() # TODO: soft delete
|
||||
|
|
|
@ -28,9 +28,10 @@ class Search(object):
|
|||
|
||||
def search(self):
|
||||
ci = CI.get_by_id(self.root_id) or abort(404, "CI <{0}> does not exist".format(self.root_id))
|
||||
ids = [self.root_id]
|
||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||
for _ in range(0, self.level):
|
||||
_tmp = list(map(json.loads, filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION))))
|
||||
print(rd.get(ids, REDIS_PREFIX_CI_RELATION))
|
||||
_tmp = list(map(json.loads, filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or [])))
|
||||
ids = [j for i in _tmp for j in i]
|
||||
if not self.orig_query or ("_type:" not in self.orig_query
|
||||
and "type_id:" not in self.orig_query
|
||||
|
@ -38,6 +39,10 @@ class Search(object):
|
|||
type_ids = CITypeRelationManager.get_child_type_ids(ci.type_id, self.level)
|
||||
self.orig_query = "_type:({0}),{1}".format(";".join(list(map(str, type_ids))), self.orig_query)
|
||||
|
||||
if not ids:
|
||||
# cis, counter, total, self.page, numfound, facet_
|
||||
return [], {}, 0, self.page, 0, {}
|
||||
|
||||
if current_app.config.get("USE_ES"):
|
||||
return SearchFromES(self.orig_query,
|
||||
fl=self.fl,
|
||||
|
@ -54,3 +59,19 @@ class Search(object):
|
|||
count=self.count,
|
||||
sort=self.sort,
|
||||
ci_ids=ids).search()
|
||||
|
||||
def statistics(self):
|
||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||
for l in range(0, self.level):
|
||||
if l == 0:
|
||||
_tmp = list(map(json.loads, [i or '[]' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))
|
||||
else:
|
||||
for idx, i in enumerate(_tmp):
|
||||
if i:
|
||||
__tmp = list(map(json.loads, filter(lambda x: x is not None,
|
||||
rd.get(i, REDIS_PREFIX_CI_RELATION) or [])))
|
||||
_tmp[idx] = [j for i in __tmp for j in i]
|
||||
else:
|
||||
_tmp[idx] = []
|
||||
|
||||
return {_id: len(_tmp[idx]) for idx, _id in enumerate(ids)}
|
||||
|
|
|
@ -133,11 +133,22 @@ class ESHandler(object):
|
|||
def update(self, ci_id, body):
|
||||
_id = self.get_index_id(ci_id)
|
||||
|
||||
if _id:
|
||||
return self.es.index(index=self.index, id=_id, body=body).get("_id")
|
||||
|
||||
def delete(self, ci_id):
|
||||
_id = self.get_index_id(ci_id)
|
||||
def create_or_update(self, ci_id, body):
|
||||
try:
|
||||
self.update(ci_id, body) or self.create(body)
|
||||
except KeyError:
|
||||
self.create(body)
|
||||
|
||||
def delete(self, ci_id):
|
||||
try:
|
||||
_id = self.get_index_id(ci_id)
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if _id:
|
||||
self.es.delete(index=self.index, id=_id)
|
||||
|
||||
def read(self, query, filter_path=None):
|
||||
|
|
|
@ -18,13 +18,13 @@ from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
|||
|
||||
@celery.task(name="cmdb.ci_cache", queue=CMDB_QUEUE)
|
||||
def ci_cache(ci_id):
|
||||
time.sleep(0.1)
|
||||
time.sleep(0.01)
|
||||
db.session.close()
|
||||
|
||||
m = api.lib.cmdb.ci.CIManager()
|
||||
ci = m.get_ci_by_id_from_db(ci_id, need_children=False, use_master=False)
|
||||
if current_app.config.get("USE_ES"):
|
||||
es.update(ci_id, ci)
|
||||
es.create_or_update(ci_id, ci)
|
||||
else:
|
||||
rd.create_or_update({ci_id: json.dumps(ci)}, REDIS_PREFIX_CI)
|
||||
|
||||
|
|
|
@ -57,6 +57,25 @@ class CIRelationSearchView(APIView):
|
|||
result=response)
|
||||
|
||||
|
||||
class CIRelationStatisticsView(APIView):
|
||||
url_prefix = "/ci_relations/statistics"
|
||||
|
||||
@auth_abandoned
|
||||
def get(self):
|
||||
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
|
||||
level = request.values.get('level', 1)
|
||||
|
||||
start = time.time()
|
||||
s = Search(root_ids, level)
|
||||
try:
|
||||
result = s.statistics()
|
||||
except SearchError as e:
|
||||
return abort(400, str(e))
|
||||
current_app.logger.debug("search time is :{0}".format(time.time() - start))
|
||||
|
||||
return self.jsonify(result)
|
||||
|
||||
|
||||
class GetSecondCIsView(APIView):
|
||||
url_prefix = "/ci_relations/<int:first_ci_id>/second_cis"
|
||||
|
||||
|
|
|
@ -13,3 +13,18 @@ export function getSecondCIs (ciId) {
|
|||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
export function searchCIRelation (params) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_relations/s?${params}`,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
||||
export function statisticsCIRelation (params) {
|
||||
return axios({
|
||||
url: '/v0.1/ci_relations/statistics',
|
||||
method: 'GET',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
</span>
|
||||
</s-table>
|
||||
|
||||
<create-instance-form ref="create" @submit="batchUpdate" />
|
||||
<create-instance-form @refresh="refreshTable" ref="create" @submit="batchUpdate" />
|
||||
</a-spin>
|
||||
</a-card>
|
||||
</template>
|
||||
|
@ -159,9 +159,11 @@ export default {
|
|||
result.totalPage = Math.ceil(res.numfound / params.pageSize)
|
||||
result.data = Object.assign([], res.result)
|
||||
result.data.forEach((item, index) => (item.key = item.ci_id))
|
||||
if (res.numfound) {
|
||||
setTimeout(() => {
|
||||
this.setColumnWidth()
|
||||
}, 200)
|
||||
}
|
||||
this.instanceList = result.data
|
||||
return result
|
||||
})
|
||||
|
@ -365,7 +367,9 @@ export default {
|
|||
that.$refs.create.visible = false
|
||||
|
||||
that.$refs.table.clearSelected()
|
||||
setTimeout(() => {
|
||||
that.$refs.table.refresh(true)
|
||||
}, 1000)
|
||||
that.reload()
|
||||
})
|
||||
.catch(e => {
|
||||
|
@ -374,7 +378,9 @@ export default {
|
|||
notification.error({
|
||||
message: e.response.data.message
|
||||
})
|
||||
setTimeout(() => {
|
||||
that.$refs.table.refresh(true)
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -399,7 +405,9 @@ export default {
|
|||
message: '删除成功'
|
||||
})
|
||||
that.$refs.table.clearSelected()
|
||||
setTimeout(() => {
|
||||
that.$refs.table.refresh(true)
|
||||
}, 1000)
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
|
@ -407,7 +415,9 @@ export default {
|
|||
notification.error({
|
||||
message: e.response.data.message
|
||||
})
|
||||
setTimeout(() => {
|
||||
that.$refs.table.refresh(true)
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -420,7 +430,9 @@ export default {
|
|||
onOk () {
|
||||
deleteCI(record.key)
|
||||
.then(res => {
|
||||
setTimeout(() => {
|
||||
that.$refs.table.refresh(true)
|
||||
}, 1000)
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<a-drawer
|
||||
:title="title + CIType.alias"
|
||||
width="500"
|
||||
@close="() => { visible = false }"
|
||||
@close="() => { visible = false; $emit('refresh', true) }"
|
||||
:visible="visible"
|
||||
:wrapStyle="{height: 'calc(100% - 108px)', overflow: 'auto', paddingBottom: '108px'}"
|
||||
>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
>{{ item[0] }}</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-alert message="管理员 还未配置关系视图!" banner v-else></a-alert>
|
||||
<a-alert message="管理员 还未配置关系视图!" banner v-else-if="relationViews.name2id && !relationViews.name2id.length"></a-alert>
|
||||
<div style="clear: both; margin-top: 20px"></div>
|
||||
<template>
|
||||
<a-row :gutter="8">
|
||||
|
@ -15,8 +15,9 @@
|
|||
<a-tree showLine :loadData="onLoadData" @select="onSelect" :treeData="treeData"></a-tree>
|
||||
</a-col>
|
||||
<a-col :span="19">
|
||||
<search-form ref="search" @refresh="refreshTable" :preferenceAttrList="preferenceAttrList" />
|
||||
<s-table
|
||||
v-if="ciTypes.length"
|
||||
v-if="levels.length > 1"
|
||||
bordered
|
||||
ref="table"
|
||||
size="middle"
|
||||
|
@ -38,9 +39,14 @@
|
|||
import { STable } from '@/components'
|
||||
|
||||
import { getRelationView, getSubscribeAttributes } from '@/api/cmdb/preference'
|
||||
import { searchCIRelation, statisticsCIRelation } from '@/api/cmdb/CIRelation'
|
||||
import { searchCI } from '@/api/cmdb/ci'
|
||||
import SearchForm from '@/views/cmdb/ci/modules/SearchForm'
|
||||
export default {
|
||||
components: { STable },
|
||||
components: {
|
||||
STable,
|
||||
SearchForm
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
treeData: [],
|
||||
|
@ -60,13 +66,12 @@ export default {
|
|||
loading: false,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
preferenceAttrList: [],
|
||||
|
||||
loadInstances: parameter => {
|
||||
console.log(parameter, 'load instances')
|
||||
const params = parameter || {}
|
||||
// const params = Object.assign(parameter, this.$refs.search.queryParam)
|
||||
let q = `q=_type:${this.typeId}`
|
||||
console.log(params, 'params')
|
||||
const params = Object.assign(parameter || {}, this.$refs.search.queryParam)
|
||||
let q = `q=_type:${this.levels[this.levels.length - 1]}`
|
||||
Object.keys(params).forEach(key => {
|
||||
if (!['pageNo', 'pageSize', 'sortField', 'sortOrder'].includes(key) && params[key] + '' !== '') {
|
||||
if (typeof params[key] === 'object' && params[key].length > 1) {
|
||||
|
@ -80,17 +85,6 @@ export default {
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (this.treeKeys.length > 0) {
|
||||
this.treeKeys.forEach((item, idx) => {
|
||||
q += `,${this.levels[idx].name}:${item}`
|
||||
})
|
||||
}
|
||||
|
||||
if (this.levels.length > this.treeKeys.length) {
|
||||
q += `&facet=${this.levels[this.treeKeys.length].name}`
|
||||
}
|
||||
|
||||
if ('pageNo' in params) {
|
||||
q += `&page=${params['pageNo']}&count=${params['pageSize']}`
|
||||
}
|
||||
|
@ -103,6 +97,7 @@ export default {
|
|||
q += `&sort=${order}${params['sortField']}`
|
||||
}
|
||||
|
||||
if (this.treeKeys.length === 0) {
|
||||
return searchCI(q).then(res => {
|
||||
const result = {}
|
||||
result.pageNo = res.page
|
||||
|
@ -111,12 +106,35 @@ export default {
|
|||
result.totalPage = Math.ceil(res.numfound / (params.pageSize || 25))
|
||||
result.data = Object.assign([], res.result)
|
||||
result.data.forEach((item, index) => (item.key = item.ci_id))
|
||||
if (res.numfound !== 0) {
|
||||
setTimeout(() => {
|
||||
this.setColumnWidth()
|
||||
}, 200)
|
||||
}
|
||||
this.loadRoot()
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
if (Object.values(res.facet).length) {
|
||||
this.wrapTreeData(res.facet)
|
||||
q += `&root_id=${this.treeKeys[this.treeKeys.length - 1]}`
|
||||
q += `&level=${this.levels.length - this.treeKeys.length}`
|
||||
if (q[0] === '&') {
|
||||
q = q.slice(1)
|
||||
}
|
||||
return searchCIRelation(q).then(res => {
|
||||
const result = {}
|
||||
result.pageNo = res.page
|
||||
result.pageSize = res.total
|
||||
result.totalCount = res.numfound
|
||||
result.totalPage = Math.ceil(res.numfound / (params.pageSize || 25))
|
||||
result.data = Object.assign([], res.result)
|
||||
result.data.forEach((item, index) => (item.key = item.ci_id))
|
||||
|
||||
if (res.numfound !== 0) {
|
||||
setTimeout(() => {
|
||||
this.setColumnWidth()
|
||||
}, 200)
|
||||
this.loadNoRoot(this.treeKeys[this.treeKeys.length - 1], this.levels.length - this.treeKeys.length - 1)
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -139,8 +157,48 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
refreshTable (bool = false) {
|
||||
this.$refs.table.refresh(bool)
|
||||
},
|
||||
|
||||
loadRoot () {
|
||||
searchCI(`q=_type:${this.levels[0]}&count=10000`).then(res => {
|
||||
const facet = []
|
||||
const ciIds = []
|
||||
res.result.forEach(item => {
|
||||
facet.push([item[item.unique], 0, item.ci_id])
|
||||
ciIds.push(item.ci_id)
|
||||
})
|
||||
statisticsCIRelation({ root_ids: ciIds.join(','), level: this.levels.length - 1 }).then(num => {
|
||||
facet.forEach((item, idx) => {
|
||||
item[1] = num[ciIds[idx] + '']
|
||||
})
|
||||
this.wrapTreeData(facet)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
loadNoRoot (rootId, level) {
|
||||
if (level === 0) {
|
||||
return
|
||||
}
|
||||
searchCIRelation(`root_id=${rootId}&level=1&count=10000`).then(res => {
|
||||
const facet = []
|
||||
const ciIds = []
|
||||
res.result.forEach(item => {
|
||||
facet.push([item[item.unique], 0, item.ci_id])
|
||||
ciIds.push(item.ci_id)
|
||||
})
|
||||
statisticsCIRelation({ root_ids: ciIds.join(','), level: level }).then(num => {
|
||||
facet.forEach((item, idx) => {
|
||||
item[1] = num[ciIds[idx] + '']
|
||||
})
|
||||
this.wrapTreeData(facet)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onSelect (keys) {
|
||||
console.log('onSelect')
|
||||
this.triggerSelect = true
|
||||
if (keys.length) {
|
||||
this.treeKeys = keys[0].split('-').filter(item => item !== '')
|
||||
|
@ -149,16 +207,15 @@ export default {
|
|||
this.$refs.table.refresh(true)
|
||||
},
|
||||
wrapTreeData (facet) {
|
||||
console.log('wrapTreeData')
|
||||
if (this.triggerSelect) {
|
||||
return
|
||||
}
|
||||
const treeData = []
|
||||
Object.values(facet)[0].forEach(item => {
|
||||
facet.forEach(item => {
|
||||
treeData.push({
|
||||
title: `${item[0]} (${item[1]})`,
|
||||
key: this.treeKeys.join('-') + '-' + item[0],
|
||||
isLeaf: this.levels.length - 1 === this.treeKeys.length
|
||||
key: this.treeKeys.join('-') + '-' + item[2],
|
||||
isLeaf: this.levels.length - 2 === this.treeKeys.length
|
||||
})
|
||||
})
|
||||
if (this.treeNode === null) {
|
||||
|
@ -176,7 +233,6 @@ export default {
|
|||
rows = document.querySelector('.ant-table-body').childNodes[0].childNodes[1].childNodes[0].childNodes
|
||||
}
|
||||
let scrollX = 0
|
||||
console.log(rows, 'rows')
|
||||
const columns = Object.assign([], this.columns)
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
columns[i].width = rows[i].offsetWidth < 80 ? 80 : rows[i].offsetWidth
|
||||
|
@ -189,7 +245,6 @@ export default {
|
|||
},
|
||||
|
||||
onLoadData (treeNode) {
|
||||
console.log(treeNode, 'load data')
|
||||
this.triggerSelect = false
|
||||
return new Promise(resolve => {
|
||||
if (treeNode.dataRef.children) {
|
||||
|
@ -215,17 +270,17 @@ export default {
|
|||
}
|
||||
})
|
||||
this.levels = this.relationViews.views[this.viewName]
|
||||
this.current = [this.levels[0]]
|
||||
this.current = [this.viewId]
|
||||
this.typeId = this.levels[0]
|
||||
console.log(this.levels, 'levels')
|
||||
// this.loadColumns()
|
||||
// this.$refs.table && this.$refs.table.refresh(true)
|
||||
this.loadColumns()
|
||||
this.$refs.table && this.$refs.table.refresh(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
loadColumns () {
|
||||
getSubscribeAttributes(this.levels[this.levels.length - 1]).then(res => {
|
||||
const prefAttrList = res.attributes
|
||||
this.preferenceAttrList = prefAttrList
|
||||
|
||||
const columns = []
|
||||
prefAttrList.forEach((item, index) => {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<a-card :bordered="false">
|
||||
<a-menu v-model="current" mode="horizontal" v-if="ciTypes.length">
|
||||
<a-menu v-model="current" mode="horizontal" v-if="ciTypes && ciTypes.length">
|
||||
<a-menu-item :key="ciType.id" v-for="ciType in ciTypes">
|
||||
<router-link
|
||||
:to="{name: 'cmdb_tree_views_item', params: {typeId: ciType.id}}"
|
||||
>{{ ciType.alias || ciTypes.name }}</router-link>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
<a-alert message="请先到 我的订阅 页面完成订阅!" banner v-else></a-alert>
|
||||
<a-alert message="请先到 我的订阅 页面完成订阅!" banner v-else-if="ciTypes && !ciTypes.length"></a-alert>
|
||||
<div style="clear: both; margin-top: 20px"></div>
|
||||
<template>
|
||||
<a-row :gutter="8">
|
||||
|
@ -15,8 +15,9 @@
|
|||
<a-tree showLine :loadData="onLoadData" @select="onSelect" :treeData="treeData"></a-tree>
|
||||
</a-col>
|
||||
<a-col :span="19">
|
||||
<search-form ref="search" @refresh="refreshTable" :preferenceAttrList="preferenceAttrList" />
|
||||
<s-table
|
||||
v-if="ciTypes.length"
|
||||
v-if="ciTypes && ciTypes.length"
|
||||
bordered
|
||||
ref="table"
|
||||
size="middle"
|
||||
|
@ -39,14 +40,18 @@ import { STable } from '@/components'
|
|||
|
||||
import { getSubscribeTreeView, getSubscribeAttributes } from '@/api/cmdb/preference'
|
||||
import { searchCI } from '@/api/cmdb/ci'
|
||||
import SearchForm from '@/views/cmdb/ci/modules/SearchForm'
|
||||
export default {
|
||||
components: { STable },
|
||||
components: {
|
||||
STable,
|
||||
SearchForm
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
treeData: [],
|
||||
triggerSelect: false,
|
||||
treeNode: null,
|
||||
ciTypes: [],
|
||||
ciTypes: null,
|
||||
levels: [],
|
||||
typeId: null,
|
||||
current: [],
|
||||
|
@ -57,10 +62,10 @@ export default {
|
|||
loading: false,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
preferenceAttrList: [],
|
||||
|
||||
loadInstances: parameter => {
|
||||
const params = parameter || {}
|
||||
// const params = Object.assign(parameter, this.$refs.search.queryParam)
|
||||
const params = Object.assign(parameter || {}, this.$refs.search.queryParam)
|
||||
let q = `q=_type:${this.typeId}`
|
||||
Object.keys(params).forEach(key => {
|
||||
if (!['pageNo', 'pageSize', 'sortField', 'sortOrder'].includes(key) && params[key] + '' !== '') {
|
||||
|
@ -134,6 +139,10 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
refreshTable (bool = false) {
|
||||
this.$refs.table.refresh(bool)
|
||||
},
|
||||
|
||||
onSelect (keys) {
|
||||
this.triggerSelect = true
|
||||
if (keys.length) {
|
||||
|
@ -210,6 +219,7 @@ export default {
|
|||
loadColumns () {
|
||||
getSubscribeAttributes(this.typeId).then(res => {
|
||||
const prefAttrList = res.attributes
|
||||
this.preferenceAttrList = prefAttrList
|
||||
|
||||
const columns = []
|
||||
prefAttrList.forEach((item, index) => {
|
||||
|
|
Loading…
Reference in New Issue