mirror of https://github.com/veops/cmdb.git
parent
91555ffa64
commit
7ef23bd779
|
@ -19,6 +19,7 @@ from api.lib.cmdb.cache import AttributeCache
|
||||||
from api.lib.cmdb.const import PermEnum
|
from api.lib.cmdb.const import PermEnum
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
|
||||||
from api.lib.cmdb.const import ResourceTypeEnum
|
from api.lib.cmdb.const import ResourceTypeEnum
|
||||||
from api.lib.cmdb.const import RoleEnum
|
from api.lib.cmdb.const import RoleEnum
|
||||||
from api.lib.cmdb.const import ValueTypeEnum
|
from api.lib.cmdb.const import ValueTypeEnum
|
||||||
|
@ -49,12 +50,17 @@ def cmdb_init_cache():
|
||||||
|
|
||||||
ci_relations = CIRelation.get_by(to_dict=False)
|
ci_relations = CIRelation.get_by(to_dict=False)
|
||||||
relations = dict()
|
relations = dict()
|
||||||
|
relations2 = dict()
|
||||||
for cr in ci_relations:
|
for cr in ci_relations:
|
||||||
relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id})
|
relations.setdefault(cr.first_ci_id, {}).update({cr.second_ci_id: cr.second_ci.type_id})
|
||||||
|
if cr.ancestor_ids:
|
||||||
|
relations2.setdefault(cr.ancestor_ids, {}).update({cr.second_ci_id: cr.second_ci.type_id})
|
||||||
for i in relations:
|
for i in relations:
|
||||||
relations[i] = json.dumps(relations[i])
|
relations[i] = json.dumps(relations[i])
|
||||||
if relations:
|
if relations:
|
||||||
rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION)
|
rd.create_or_update(relations, REDIS_PREFIX_CI_RELATION)
|
||||||
|
if relations2:
|
||||||
|
rd.create_or_update(relations2, REDIS_PREFIX_CI_RELATION2)
|
||||||
|
|
||||||
es = None
|
es = None
|
||||||
if current_app.config.get("USE_ES"):
|
if current_app.config.get("USE_ES"):
|
||||||
|
|
|
@ -182,6 +182,9 @@ class CIManager(object):
|
||||||
need_children and res.update(CIRelationManager.get_children(ci_id, ret_key=ret_key)) # one floor
|
need_children and res.update(CIRelationManager.get_children(ci_id, ret_key=ret_key)) # one floor
|
||||||
|
|
||||||
ci_type = CITypeCache.get(ci.type_id)
|
ci_type = CITypeCache.get(ci.type_id)
|
||||||
|
if not ci_type:
|
||||||
|
return res
|
||||||
|
|
||||||
res["ci_type"] = ci_type.name
|
res["ci_type"] = ci_type.name
|
||||||
|
|
||||||
fields = CITypeAttributeManager.get_attr_names_by_type_id(ci.type_id) if not fields else fields
|
fields = CITypeAttributeManager.get_attr_names_by_type_id(ci.type_id) if not fields else fields
|
||||||
|
@ -518,11 +521,13 @@ class CIManager(object):
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
for item in CIRelation.get_by(first_ci_id=ci_id, to_dict=False):
|
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)
|
ci_relation_delete.apply_async(
|
||||||
|
args=(item.first_ci_id, item.second_ci_id, item.ancestor_ids), queue=CMDB_QUEUE)
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
for item in CIRelation.get_by(second_ci_id=ci_id, to_dict=False):
|
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)
|
ci_relation_delete.apply_async(
|
||||||
|
args=(item.first_ci_id, item.second_ci_id, item.ancestor_ids), queue=CMDB_QUEUE)
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
ad_ci = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True)
|
ad_ci = AutoDiscoveryCI.get_by(ci_id=ci_id, to_dict=False, first=True)
|
||||||
|
@ -886,12 +891,14 @@ class CIRelationManager(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ancestor_ids(cls, ci_ids, level=1):
|
def get_ancestor_ids(cls, ci_ids, level=1):
|
||||||
for _ in range(level):
|
level2ids = dict()
|
||||||
cis = db.session.query(CIRelation.first_ci_id).filter(
|
for _level in range(1, level + 1):
|
||||||
|
cis = db.session.query(CIRelation.first_ci_id, CIRelation.ancestor_ids).filter(
|
||||||
CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False))
|
CIRelation.second_ci_id.in_(ci_ids)).filter(CIRelation.deleted.is_(False))
|
||||||
ci_ids = [i.first_ci_id for i in cis]
|
ci_ids = [i.first_ci_id for i in cis]
|
||||||
|
level2ids[_level + 1] = {int(i.ancestor_ids.split(',')[-1]) for i in cis if i.ancestor_ids}
|
||||||
|
|
||||||
return ci_ids
|
return ci_ids, level2ids
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation):
|
def _check_constraint(first_ci_id, first_type_id, second_ci_id, second_type_id, type_relation):
|
||||||
|
@ -918,13 +925,14 @@ class CIRelationManager(object):
|
||||||
return abort(400, ErrFormat.relation_constraint.format("1-N"))
|
return abort(400, ErrFormat.relation_constraint.format("1-N"))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None):
|
def add(cls, first_ci_id, second_ci_id, more=None, relation_type_id=None, ancestor_ids=None):
|
||||||
|
|
||||||
first_ci = CIManager.confirm_ci_existed(first_ci_id)
|
first_ci = CIManager.confirm_ci_existed(first_ci_id)
|
||||||
second_ci = CIManager.confirm_ci_existed(second_ci_id)
|
second_ci = CIManager.confirm_ci_existed(second_ci_id)
|
||||||
|
|
||||||
existed = CIRelation.get_by(first_ci_id=first_ci_id,
|
existed = CIRelation.get_by(first_ci_id=first_ci_id,
|
||||||
second_ci_id=second_ci_id,
|
second_ci_id=second_ci_id,
|
||||||
|
ancestor_ids=ancestor_ids,
|
||||||
to_dict=False,
|
to_dict=False,
|
||||||
first=True)
|
first=True)
|
||||||
if existed is not None:
|
if existed is not None:
|
||||||
|
@ -960,11 +968,12 @@ class CIRelationManager(object):
|
||||||
|
|
||||||
existed = CIRelation.create(first_ci_id=first_ci_id,
|
existed = CIRelation.create(first_ci_id=first_ci_id,
|
||||||
second_ci_id=second_ci_id,
|
second_ci_id=second_ci_id,
|
||||||
relation_type_id=relation_type_id)
|
relation_type_id=relation_type_id,
|
||||||
|
ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
CIRelationHistoryManager().add(existed, OperateType.ADD)
|
CIRelationHistoryManager().add(existed, OperateType.ADD)
|
||||||
|
|
||||||
ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
|
ci_relation_cache.apply_async(args=(first_ci_id, second_ci_id, ancestor_ids), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
if more is not None:
|
if more is not None:
|
||||||
existed.upadte(more=more)
|
existed.upadte(more=more)
|
||||||
|
@ -988,53 +997,56 @@ class CIRelationManager(object):
|
||||||
his_manager = CIRelationHistoryManager()
|
his_manager = CIRelationHistoryManager()
|
||||||
his_manager.add(cr, operate_type=OperateType.DELETE)
|
his_manager.add(cr, operate_type=OperateType.DELETE)
|
||||||
|
|
||||||
ci_relation_delete.apply_async(args=(cr.first_ci_id, cr.second_ci_id), queue=CMDB_QUEUE)
|
ci_relation_delete.apply_async(args=(cr.first_ci_id, cr.second_ci_id, cr.ancestor_ids), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
return cr_id
|
return cr_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_2(cls, first_ci_id, second_ci_id):
|
def delete_2(cls, first_ci_id, second_ci_id, ancestor_ids=None):
|
||||||
cr = CIRelation.get_by(first_ci_id=first_ci_id,
|
cr = CIRelation.get_by(first_ci_id=first_ci_id,
|
||||||
second_ci_id=second_ci_id,
|
second_ci_id=second_ci_id,
|
||||||
|
ancestor_ids=ancestor_ids,
|
||||||
to_dict=False,
|
to_dict=False,
|
||||||
first=True)
|
first=True)
|
||||||
|
|
||||||
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id), queue=CMDB_QUEUE)
|
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id, ancestor_ids), queue=CMDB_QUEUE)
|
||||||
|
|
||||||
return cls.delete(cr.id)
|
return cr and cls.delete(cr.id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def batch_update(cls, ci_ids, parents, children):
|
def batch_update(cls, ci_ids, parents, children, ancestor_ids=None):
|
||||||
"""
|
"""
|
||||||
only for many to one
|
only for many to one
|
||||||
:param ci_ids:
|
:param ci_ids:
|
||||||
:param parents:
|
:param parents:
|
||||||
:param children:
|
:param children:
|
||||||
|
:param ancestor_ids:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
if isinstance(parents, list):
|
if isinstance(parents, list):
|
||||||
for parent_id in parents:
|
for parent_id in parents:
|
||||||
for ci_id in ci_ids:
|
for ci_id in ci_ids:
|
||||||
cls.add(parent_id, ci_id)
|
cls.add(parent_id, ci_id, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
if isinstance(children, list):
|
if isinstance(children, list):
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
for ci_id in ci_ids:
|
for ci_id in ci_ids:
|
||||||
cls.add(ci_id, child_id)
|
cls.add(ci_id, child_id, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def batch_delete(cls, ci_ids, parents):
|
def batch_delete(cls, ci_ids, parents, ancestor_ids=None):
|
||||||
"""
|
"""
|
||||||
only for many to one
|
only for many to one
|
||||||
:param ci_ids:
|
:param ci_ids:
|
||||||
:param parents:
|
:param parents:
|
||||||
|
:param ancestor_ids:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(parents, list):
|
if isinstance(parents, list):
|
||||||
for parent_id in parents:
|
for parent_id in parents:
|
||||||
for ci_id in ci_ids:
|
for ci_id in ci_ids:
|
||||||
cls.delete_2(parent_id, ci_id)
|
cls.delete_2(parent_id, ci_id, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
|
|
||||||
class CITriggerManager(object):
|
class CITriggerManager(object):
|
||||||
|
|
|
@ -637,6 +637,16 @@ class CITypeRelationManager(object):
|
||||||
current_app.logger.warning(str(e))
|
current_app.logger.warning(str(e))
|
||||||
return abort(400, ErrFormat.circular_dependency_error)
|
return abort(400, ErrFormat.circular_dependency_error)
|
||||||
|
|
||||||
|
if constraint == ConstraintEnum.Many2Many:
|
||||||
|
other_c = CITypeRelation.get_by(parent_id=p.id, constraint=ConstraintEnum.Many2Many,
|
||||||
|
to_dict=False, first=True)
|
||||||
|
other_p = CITypeRelation.get_by(child_id=c.id, constraint=ConstraintEnum.Many2Many,
|
||||||
|
to_dict=False, first=True)
|
||||||
|
if other_c and other_c.child_id != c.id:
|
||||||
|
return abort(400, ErrFormat.m2m_relation_constraint.format(p.name, other_c.child.name))
|
||||||
|
if other_p and other_p.parent_id != p.id:
|
||||||
|
return abort(400, ErrFormat.m2m_relation_constraint.format(other_p.parent.name, c.name))
|
||||||
|
|
||||||
existed = cls._get(p.id, c.id)
|
existed = cls._get(p.id, c.id)
|
||||||
if existed is not None:
|
if existed is not None:
|
||||||
existed.update(relation_type_id=relation_type_id,
|
existed.update(relation_type_id=relation_type_id,
|
||||||
|
@ -686,6 +696,24 @@ class CITypeRelationManager(object):
|
||||||
|
|
||||||
cls.delete(ctr.id)
|
cls.delete(ctr.id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_level2constraint(root_id, level):
|
||||||
|
level = level + 1 if level == 1 else level
|
||||||
|
ci = CI.get_by_id(root_id)
|
||||||
|
if ci is None:
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
root_id = ci.type_id
|
||||||
|
level2constraint = dict()
|
||||||
|
for lv in range(1, int(level) + 1):
|
||||||
|
for i in CITypeRelation.get_by(parent_id=root_id, to_dict=False):
|
||||||
|
if i.constraint == ConstraintEnum.Many2Many:
|
||||||
|
root_id = i.child_id
|
||||||
|
level2constraint[lv] = ConstraintEnum.Many2Many
|
||||||
|
break
|
||||||
|
|
||||||
|
return level2constraint
|
||||||
|
|
||||||
|
|
||||||
class CITypeAttributeGroupManager(object):
|
class CITypeAttributeGroupManager(object):
|
||||||
cls = CITypeAttributeGroup
|
cls = CITypeAttributeGroup
|
||||||
|
|
|
@ -100,6 +100,7 @@ class AttributeDefaultValueEnum(BaseEnum):
|
||||||
CMDB_QUEUE = "one_cmdb_async"
|
CMDB_QUEUE = "one_cmdb_async"
|
||||||
REDIS_PREFIX_CI = "ONE_CMDB"
|
REDIS_PREFIX_CI = "ONE_CMDB"
|
||||||
REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION"
|
REDIS_PREFIX_CI_RELATION = "CMDB_CI_RELATION"
|
||||||
|
REDIS_PREFIX_CI_RELATION2 = "CMDB_CI_RELATION2"
|
||||||
|
|
||||||
BUILTIN_KEYWORDS = {'id', '_id', 'ci_id', 'type', '_type', 'ci_type'}
|
BUILTIN_KEYWORDS = {'id', '_id', 'ci_id', 'type', '_type', 'ci_type'}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ from api.lib.cmdb.attribute import AttributeManager
|
||||||
from api.lib.cmdb.cache import AttributeCache
|
from api.lib.cmdb.cache import AttributeCache
|
||||||
from api.lib.cmdb.cache import CITypeAttributesCache
|
from api.lib.cmdb.cache import CITypeAttributesCache
|
||||||
from api.lib.cmdb.cache import CITypeCache
|
from api.lib.cmdb.cache import CITypeCache
|
||||||
from api.lib.cmdb.const import PermEnum, ResourceTypeEnum, RoleEnum
|
from api.lib.cmdb.const import ConstraintEnum
|
||||||
|
from api.lib.cmdb.const import PermEnum
|
||||||
|
from api.lib.cmdb.const import ResourceTypeEnum
|
||||||
|
from api.lib.cmdb.const import RoleEnum
|
||||||
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
from api.lib.cmdb.perms import CIFilterPermsCRUD
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.exception import AbortException
|
from api.lib.exception import AbortException
|
||||||
|
@ -229,14 +232,28 @@ class PreferenceManager(object):
|
||||||
if not parents:
|
if not parents:
|
||||||
return
|
return
|
||||||
|
|
||||||
for l in leaf:
|
for _l in leaf:
|
||||||
_find_parent(l)
|
_find_parent(_l)
|
||||||
|
|
||||||
for node_id in node2show_types:
|
for node_id in node2show_types:
|
||||||
node2show_types[node_id] = [CITypeCache.get(i).to_dict() for i in set(node2show_types[node_id])]
|
node2show_types[node_id] = [CITypeCache.get(i).to_dict() for i in set(node2show_types[node_id])]
|
||||||
|
|
||||||
|
topo_flatten = list(toposort.toposort_flatten(topo))
|
||||||
|
level2constraint = {}
|
||||||
|
for i, _ in enumerate(topo_flatten[1:]):
|
||||||
|
ctr = CITypeRelation.get_by(
|
||||||
|
parent_id=topo_flatten[i], child_id=topo_flatten[i + 1], first=True, to_dict=False)
|
||||||
|
level2constraint[i + 1] = ctr and ctr.constraint
|
||||||
|
|
||||||
|
if leaf2show_types.get(topo_flatten[-1]):
|
||||||
|
ctr = CITypeRelation.get_by(
|
||||||
|
parent_id=topo_flatten[-1],
|
||||||
|
child_id=leaf2show_types[topo_flatten[-1]][0], first=True, to_dict=False)
|
||||||
|
level2constraint[len(topo_flatten)] = ctr and ctr.constraint
|
||||||
|
|
||||||
result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))),
|
result[view_name] = dict(topo=list(map(list, toposort.toposort(topo))),
|
||||||
topo_flatten=list(toposort.toposort_flatten(topo)),
|
topo_flatten=topo_flatten,
|
||||||
|
level2constraint=level2constraint,
|
||||||
leaf=leaf,
|
leaf=leaf,
|
||||||
leaf2show_types=leaf2show_types,
|
leaf2show_types=leaf2show_types,
|
||||||
node2show_types=node2show_types,
|
node2show_types=node2show_types,
|
||||||
|
@ -338,3 +355,29 @@ class PreferenceManager(object):
|
||||||
|
|
||||||
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
|
for i in PreferenceTreeView.get_by(type_id=type_id, uid=uid, to_dict=False):
|
||||||
i.soft_delete()
|
i.soft_delete()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def can_edit_relation(parent_id, child_id):
|
||||||
|
views = PreferenceRelationView.get_by(to_dict=False)
|
||||||
|
for view in views:
|
||||||
|
has_m2m = False
|
||||||
|
last_node_id = None
|
||||||
|
for cr in view.cr_ids:
|
||||||
|
_rel = CITypeRelation.get_by(parent_id=cr['parent_id'], child_id=cr['child_id'],
|
||||||
|
first=True, to_dict=False)
|
||||||
|
if _rel and _rel.constraint == ConstraintEnum.Many2Many:
|
||||||
|
has_m2m = True
|
||||||
|
|
||||||
|
if parent_id == _rel.parent_id and child_id == _rel.child_id:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if _rel:
|
||||||
|
last_node_id = _rel.child_id
|
||||||
|
|
||||||
|
if parent_id == last_node_id:
|
||||||
|
rels = CITypeRelation.get_by(parent_id=last_node_id, to_dict=False)
|
||||||
|
for rel in rels:
|
||||||
|
if rel.child_id == child_id and has_m2m:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
|
@ -31,6 +31,7 @@ class ErrFormat(CommonErrFormat):
|
||||||
unique_key_required = "主键字段 {} 缺失"
|
unique_key_required = "主键字段 {} 缺失"
|
||||||
ci_is_already_existed = "CI 已经存在!"
|
ci_is_already_existed = "CI 已经存在!"
|
||||||
relation_constraint = "关系约束: {}, 校验失败 "
|
relation_constraint = "关系约束: {}, 校验失败 "
|
||||||
|
m2m_relation_constraint = "多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!"
|
||||||
relation_not_found = "CI关系: {} 不存在"
|
relation_not_found = "CI关系: {} 不存在"
|
||||||
ci_search_Parentheses_invalid = "搜索表达式里小括号前不支持: 或、非"
|
ci_search_Parentheses_invalid = "搜索表达式里小括号前不支持: 或、非"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
|
@ -10,11 +8,14 @@ from flask import current_app
|
||||||
from api.extensions import rd
|
from api.extensions import rd
|
||||||
from api.lib.cmdb.ci import CIRelationManager
|
from api.lib.cmdb.ci import CIRelationManager
|
||||||
from api.lib.cmdb.ci_type import CITypeRelationManager
|
from api.lib.cmdb.ci_type import CITypeRelationManager
|
||||||
|
from api.lib.cmdb.const import ConstraintEnum
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB
|
from api.lib.cmdb.search.ci.db.search import Search as SearchFromDB
|
||||||
from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
|
from api.lib.cmdb.search.ci.es.search import Search as SearchFromES
|
||||||
from api.models.cmdb import CI
|
from api.models.cmdb import CI
|
||||||
|
from api.models.cmdb import CIRelation
|
||||||
|
|
||||||
|
|
||||||
class Search(object):
|
class Search(object):
|
||||||
|
@ -26,7 +27,8 @@ class Search(object):
|
||||||
page=1,
|
page=1,
|
||||||
count=None,
|
count=None,
|
||||||
sort=None,
|
sort=None,
|
||||||
reverse=False):
|
reverse=False,
|
||||||
|
ancestor_ids=None):
|
||||||
self.orig_query = query
|
self.orig_query = query
|
||||||
self.fl = fl
|
self.fl = fl
|
||||||
self.facet_field = facet_field
|
self.facet_field = facet_field
|
||||||
|
@ -38,25 +40,81 @@ class Search(object):
|
||||||
self.level = level or 0
|
self.level = level or 0
|
||||||
self.reverse = reverse
|
self.reverse = reverse
|
||||||
|
|
||||||
def _get_ids(self):
|
self.level2constraint = CITypeRelationManager.get_level2constraint(
|
||||||
|
root_id[0] if root_id and isinstance(root_id, list) else root_id,
|
||||||
|
level[0] if isinstance(level, list) and level else level)
|
||||||
|
|
||||||
|
self.ancestor_ids = ancestor_ids
|
||||||
|
self.has_m2m = False
|
||||||
|
if self.ancestor_ids:
|
||||||
|
self.has_m2m = True
|
||||||
|
else:
|
||||||
|
level = level[0] if isinstance(level, list) and level else level
|
||||||
|
for _l, c in self.level2constraint.items():
|
||||||
|
if _l < int(level) and c == ConstraintEnum.Many2Many:
|
||||||
|
self.has_m2m = True
|
||||||
|
|
||||||
|
def _get_ids(self, ids):
|
||||||
|
if self.level[-1] == 1 and len(ids) == 1:
|
||||||
|
if self.ancestor_ids is None:
|
||||||
|
return [i.second_ci_id for i in CIRelation.get_by(first_ci_id=ids[0], to_dict=False)]
|
||||||
|
|
||||||
|
else:
|
||||||
|
seconds = {i.second_ci_id for i in CIRelation.get_by(first_ci_id=ids[0],
|
||||||
|
ancestor_ids=self.ancestor_ids,
|
||||||
|
to_dict=False)}
|
||||||
|
|
||||||
|
return list(seconds)
|
||||||
|
|
||||||
merge_ids = []
|
merge_ids = []
|
||||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
key = []
|
||||||
|
_tmp = []
|
||||||
for level in range(1, sorted(self.level)[-1] + 1):
|
for level in range(1, sorted(self.level)[-1] + 1):
|
||||||
_tmp = list(map(lambda x: list(json.loads(x).keys()),
|
if not self.has_m2m:
|
||||||
filter(lambda x: x is not None, rd.get(ids, REDIS_PREFIX_CI_RELATION) or [])))
|
_tmp = map(lambda x: json.loads(x).keys(),
|
||||||
|
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]
|
||||||
|
key, prefix = ids, REDIS_PREFIX_CI_RELATION
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not self.ancestor_ids:
|
||||||
|
if level == 1:
|
||||||
|
key, prefix = list(map(str, ids)), REDIS_PREFIX_CI_RELATION
|
||||||
|
else:
|
||||||
|
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
|
||||||
|
prefix = REDIS_PREFIX_CI_RELATION2
|
||||||
|
else:
|
||||||
|
if level == 1:
|
||||||
|
key, prefix = ["{},{}".format(self.ancestor_ids, i) for i in ids], REDIS_PREFIX_CI_RELATION2
|
||||||
|
else:
|
||||||
|
key = list(set(["{},{}".format(i, j) for idx, i in enumerate(key) for j in _tmp[idx]]))
|
||||||
|
prefix = REDIS_PREFIX_CI_RELATION2
|
||||||
|
|
||||||
|
if not key:
|
||||||
|
return []
|
||||||
|
|
||||||
|
_tmp = list(map(lambda x: json.loads(x).keys() if x else [], rd.get(key, prefix) or []))
|
||||||
ids = [j for i in _tmp for j in i]
|
ids = [j for i in _tmp for j in i]
|
||||||
|
|
||||||
if level in self.level:
|
if level in self.level:
|
||||||
merge_ids.extend(ids)
|
merge_ids.extend(ids)
|
||||||
|
|
||||||
return merge_ids
|
return merge_ids
|
||||||
|
|
||||||
def _get_reverse_ids(self):
|
def _get_reverse_ids(self, ids):
|
||||||
merge_ids = []
|
merge_ids = []
|
||||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
level2ids = {}
|
||||||
for level in range(1, sorted(self.level)[-1] + 1):
|
for level in range(1, sorted(self.level)[-1] + 1):
|
||||||
ids = CIRelationManager.get_ancestor_ids(ids, 1)
|
ids, _level2ids = CIRelationManager.get_ancestor_ids(ids, 1)
|
||||||
|
|
||||||
|
if _level2ids.get(2):
|
||||||
|
level2ids[level + 1] = _level2ids[2]
|
||||||
|
|
||||||
if level in self.level:
|
if level in self.level:
|
||||||
merge_ids.extend(ids)
|
if level in level2ids and level2ids[level]:
|
||||||
|
merge_ids.extend(set(ids) & set(level2ids[level]))
|
||||||
|
else:
|
||||||
|
merge_ids.extend(ids)
|
||||||
|
|
||||||
return merge_ids
|
return merge_ids
|
||||||
|
|
||||||
|
@ -64,7 +122,7 @@ class Search(object):
|
||||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||||
cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids]
|
cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids]
|
||||||
|
|
||||||
merge_ids = self._get_ids() if not self.reverse else self._get_reverse_ids()
|
merge_ids = self._get_ids(ids) if not self.reverse else self._get_reverse_ids(ids)
|
||||||
|
|
||||||
if not self.orig_query or ("_type:" not in self.orig_query
|
if not self.orig_query or ("_type:" not in self.orig_query
|
||||||
and "type_id:" not in self.orig_query
|
and "type_id:" not in self.orig_query
|
||||||
|
@ -76,11 +134,11 @@ class Search(object):
|
||||||
type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level))
|
type_ids.extend(CITypeRelationManager.get_child_type_ids(ci.type_id, level))
|
||||||
else:
|
else:
|
||||||
type_ids.extend(CITypeRelationManager.get_parent_type_ids(ci.type_id, level))
|
type_ids.extend(CITypeRelationManager.get_parent_type_ids(ci.type_id, level))
|
||||||
type_ids = list(set(type_ids))
|
type_ids = set(type_ids)
|
||||||
if self.orig_query:
|
if self.orig_query:
|
||||||
self.orig_query = "_type:({0}),{1}".format(";".join(list(map(str, type_ids))), self.orig_query)
|
self.orig_query = "_type:({0}),{1}".format(";".join(map(str, type_ids)), self.orig_query)
|
||||||
else:
|
else:
|
||||||
self.orig_query = "_type:({0})".format(";".join(list(map(str, type_ids))))
|
self.orig_query = "_type:({0})".format(";".join(map(str, type_ids)))
|
||||||
|
|
||||||
if not merge_ids:
|
if not merge_ids:
|
||||||
# cis, counter, total, self.page, numfound, facet_
|
# cis, counter, total, self.page, numfound, facet_
|
||||||
|
@ -105,35 +163,65 @@ class Search(object):
|
||||||
|
|
||||||
def statistics(self, type_ids):
|
def statistics(self, type_ids):
|
||||||
self.level = int(self.level)
|
self.level = int(self.level)
|
||||||
_tmp = []
|
|
||||||
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
|
||||||
for lv in range(0, self.level):
|
_tmp = []
|
||||||
if not lv:
|
level2ids = {}
|
||||||
if type_ids and lv == self.level - 1:
|
for lv in range(1, self.level + 1):
|
||||||
|
level2ids[lv] = []
|
||||||
|
|
||||||
|
if lv == 1:
|
||||||
|
if not self.has_m2m:
|
||||||
|
key, prefix = ids, REDIS_PREFIX_CI_RELATION
|
||||||
|
else:
|
||||||
|
if not self.ancestor_ids:
|
||||||
|
key, prefix = ids, REDIS_PREFIX_CI_RELATION
|
||||||
|
else:
|
||||||
|
key = ["{},{}".format(self.ancestor_ids, _id) for _id in ids]
|
||||||
|
prefix = REDIS_PREFIX_CI_RELATION2
|
||||||
|
|
||||||
|
level2ids[lv] = [[i] for i in key]
|
||||||
|
|
||||||
|
if not key:
|
||||||
|
_tmp = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
if type_ids and lv == self.level:
|
||||||
_tmp = list(map(lambda x: [i for i in x if i[1] in type_ids],
|
_tmp = list(map(lambda x: [i for i in x if i[1] in type_ids],
|
||||||
(map(lambda x: list(json.loads(x).items()),
|
(map(lambda x: list(json.loads(x).items()),
|
||||||
[i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))))
|
[i or '{}' for i in rd.get(key, prefix) or []]))))
|
||||||
else:
|
else:
|
||||||
_tmp = list(map(lambda x: list(json.loads(x).items()),
|
_tmp = list(map(lambda x: list(json.loads(x).items()),
|
||||||
[i or '{}' for i in rd.get(ids, REDIS_PREFIX_CI_RELATION) or []]))
|
[i or '{}' for i in rd.get(key, prefix) or []]))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for idx, item in enumerate(_tmp):
|
for idx, item in enumerate(_tmp):
|
||||||
if item:
|
if item:
|
||||||
if type_ids and lv == self.level - 1:
|
if not self.has_m2m:
|
||||||
__tmp = list(
|
key, prefix = [i[0] for i in item], REDIS_PREFIX_CI_RELATION
|
||||||
map(lambda x: [(_id, type_id) for _id, type_id in json.loads(x).items()
|
|
||||||
if type_id in type_ids],
|
|
||||||
filter(lambda x: x is not None,
|
|
||||||
rd.get([i[0] for i in item], REDIS_PREFIX_CI_RELATION) or [])))
|
|
||||||
else:
|
else:
|
||||||
|
key = list(set(['{},{}'.format(j, i[0]) for i in item for j in level2ids[lv - 1][idx]]))
|
||||||
|
prefix = REDIS_PREFIX_CI_RELATION2
|
||||||
|
|
||||||
__tmp = list(map(lambda x: list(json.loads(x).items()),
|
level2ids[lv].append(key)
|
||||||
filter(lambda x: x is not None,
|
|
||||||
rd.get([i[0] for i in item], REDIS_PREFIX_CI_RELATION) or [])))
|
if key:
|
||||||
|
if type_ids and lv == self.level:
|
||||||
|
__tmp = map(lambda x: [(_id, type_id) for _id, type_id in json.loads(x).items()
|
||||||
|
if type_id in type_ids],
|
||||||
|
filter(lambda x: x is not None,
|
||||||
|
rd.get(key, prefix) or []))
|
||||||
|
else:
|
||||||
|
__tmp = map(lambda x: list(json.loads(x).items()),
|
||||||
|
filter(lambda x: x is not None,
|
||||||
|
rd.get(key, prefix) or []))
|
||||||
|
else:
|
||||||
|
__tmp = []
|
||||||
|
|
||||||
_tmp[idx] = [j for i in __tmp for j in i]
|
_tmp[idx] = [j for i in __tmp for j in i]
|
||||||
else:
|
else:
|
||||||
_tmp[idx] = []
|
_tmp[idx] = []
|
||||||
|
level2ids[lv].append([])
|
||||||
|
|
||||||
result = {str(_id): len(_tmp[idx]) for idx, _id in enumerate(ids)}
|
result = {str(_id): len(_tmp[idx]) for idx, _id in enumerate(ids)}
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,8 @@ class CIRelation(Model):
|
||||||
relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False)
|
relation_type_id = db.Column(db.Integer, db.ForeignKey("c_relation_types.id"), nullable=False)
|
||||||
more = db.Column(db.Integer, db.ForeignKey("c_cis.id"))
|
more = db.Column(db.Integer, db.ForeignKey("c_cis.id"))
|
||||||
|
|
||||||
|
ancestor_ids = db.Column(db.String(128), index=True)
|
||||||
|
|
||||||
first_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.first_ci_id")
|
first_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.first_ci_id")
|
||||||
second_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.second_ci_id")
|
second_ci = db.relationship("CI", primaryjoin="CI.id==CIRelation.second_ci_id")
|
||||||
relation_type = db.relationship("RelationType", backref="c_ci_relations.relation_type_id")
|
relation_type = db.relationship("RelationType", backref="c_ci_relations.relation_type_id")
|
||||||
|
|
|
@ -16,6 +16,7 @@ from api.lib.cmdb.cache import CITypeAttributesCache
|
||||||
from api.lib.cmdb.const import CMDB_QUEUE
|
from api.lib.cmdb.const import CMDB_QUEUE
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
from api.lib.cmdb.const import REDIS_PREFIX_CI
|
||||||
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION
|
||||||
|
from api.lib.cmdb.const import REDIS_PREFIX_CI_RELATION2
|
||||||
from api.lib.decorator import flush_db
|
from api.lib.decorator import flush_db
|
||||||
from api.lib.decorator import reconnect_db
|
from api.lib.decorator import reconnect_db
|
||||||
from api.lib.perm.acl.cache import UserCache
|
from api.lib.perm.acl.cache import UserCache
|
||||||
|
@ -97,16 +98,30 @@ def ci_delete_trigger(trigger, operate_type, ci_dict):
|
||||||
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_relation_cache", queue=CMDB_QUEUE)
|
||||||
@flush_db
|
@flush_db
|
||||||
@reconnect_db
|
@reconnect_db
|
||||||
def ci_relation_cache(parent_id, child_id):
|
def ci_relation_cache(parent_id, child_id, ancestor_ids):
|
||||||
with Lock("CIRelation_{}".format(parent_id)):
|
with Lock("CIRelation_{}".format(parent_id)):
|
||||||
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
if ancestor_ids is None:
|
||||||
children = json.loads(children) if children is not None else {}
|
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
||||||
|
children = json.loads(children) if children is not None else {}
|
||||||
|
|
||||||
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, first=True, to_dict=False)
|
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
|
||||||
if str(child_id) not in children:
|
first=True, to_dict=False)
|
||||||
children[str(child_id)] = cr.second_ci.type_id
|
if str(child_id) not in children:
|
||||||
|
children[str(child_id)] = cr.second_ci.type_id
|
||||||
|
|
||||||
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
||||||
|
|
||||||
|
else:
|
||||||
|
key = "{},{}".format(ancestor_ids, parent_id)
|
||||||
|
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
|
||||||
|
grandson = json.loads(grandson) if grandson is not None else {}
|
||||||
|
|
||||||
|
cr = CIRelation.get_by(first_ci_id=parent_id, second_ci_id=child_id, ancestor_ids=ancestor_ids,
|
||||||
|
first=True, to_dict=False)
|
||||||
|
if cr and str(cr.second_ci_id) not in grandson:
|
||||||
|
grandson[str(cr.second_ci_id)] = cr.second_ci.type_id
|
||||||
|
|
||||||
|
rd.create_or_update({key: json.dumps(grandson)}, REDIS_PREFIX_CI_RELATION2)
|
||||||
|
|
||||||
current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
current_app.logger.info("ADD ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
||||||
|
|
||||||
|
@ -156,20 +171,31 @@ def ci_relation_add(parent_dict, child_id, uid):
|
||||||
try:
|
try:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
except:
|
except:
|
||||||
pass
|
db.session.rollback()
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
|
@celery.task(name="cmdb.ci_relation_delete", queue=CMDB_QUEUE)
|
||||||
@reconnect_db
|
@reconnect_db
|
||||||
def ci_relation_delete(parent_id, child_id):
|
def ci_relation_delete(parent_id, child_id, ancestor_ids):
|
||||||
with Lock("CIRelation_{}".format(parent_id)):
|
with Lock("CIRelation_{}".format(parent_id)):
|
||||||
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
if ancestor_ids is None:
|
||||||
children = json.loads(children) if children is not None else {}
|
children = rd.get([parent_id], REDIS_PREFIX_CI_RELATION)[0]
|
||||||
|
children = json.loads(children) if children is not None else {}
|
||||||
|
|
||||||
if str(child_id) in children:
|
if str(child_id) in children:
|
||||||
children.pop(str(child_id))
|
children.pop(str(child_id))
|
||||||
|
|
||||||
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
rd.create_or_update({parent_id: json.dumps(children)}, REDIS_PREFIX_CI_RELATION)
|
||||||
|
|
||||||
|
else:
|
||||||
|
key = "{},{}".format(ancestor_ids, parent_id)
|
||||||
|
grandson = rd.get([key], REDIS_PREFIX_CI_RELATION2)[0]
|
||||||
|
grandson = json.loads(grandson) if grandson is not None else {}
|
||||||
|
|
||||||
|
if str(child_id) in grandson:
|
||||||
|
grandson.pop(str(child_id))
|
||||||
|
|
||||||
|
rd.create_or_update({key: json.dumps(grandson)}, REDIS_PREFIX_CI_RELATION2)
|
||||||
|
|
||||||
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
current_app.logger.info("DELETE ci relation cache: {0} -> {1}".format(parent_id, child_id))
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ class CIRelationSearchView(APIView):
|
||||||
count = get_page_size(request.values.get("count") or request.values.get("page_size"))
|
count = get_page_size(request.values.get("count") or request.values.get("page_size"))
|
||||||
|
|
||||||
root_id = request.values.get('root_id')
|
root_id = request.values.get('root_id')
|
||||||
|
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
|
||||||
level = list(map(int, handle_arg_list(request.values.get('level', '1'))))
|
level = list(map(int, handle_arg_list(request.values.get('level', '1'))))
|
||||||
|
|
||||||
query = request.values.get('q', "")
|
query = request.values.get('q', "")
|
||||||
|
@ -44,7 +45,7 @@ class CIRelationSearchView(APIView):
|
||||||
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
reverse = request.values.get("reverse") in current_app.config.get('BOOL_TRUE')
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse)
|
s = Search(root_id, level, query, fl, facet, page, count, sort, reverse, ancestor_ids=ancestor_ids)
|
||||||
try:
|
try:
|
||||||
response, counter, total, page, numfound, facet = s.search()
|
response, counter, total, page, numfound, facet = s.search()
|
||||||
except SearchError as e:
|
except SearchError as e:
|
||||||
|
@ -67,9 +68,10 @@ class CIRelationStatisticsView(APIView):
|
||||||
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
|
root_ids = list(map(int, handle_arg_list(request.values.get('root_ids'))))
|
||||||
level = request.values.get('level', 1)
|
level = request.values.get('level', 1)
|
||||||
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
|
type_ids = set(map(int, handle_arg_list(request.values.get('type_ids', []))))
|
||||||
|
ancestor_ids = request.values.get('ancestor_ids') or None # only for many to many
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
s = Search(root_ids, level)
|
s = Search(root_ids, level, ancestor_ids=ancestor_ids)
|
||||||
try:
|
try:
|
||||||
result = s.statistics(type_ids)
|
result = s.statistics(type_ids)
|
||||||
except SearchError as e:
|
except SearchError as e:
|
||||||
|
@ -121,14 +123,18 @@ class CIRelationView(APIView):
|
||||||
url_prefix = "/ci_relations/<int:first_ci_id>/<int:second_ci_id>"
|
url_prefix = "/ci_relations/<int:first_ci_id>/<int:second_ci_id>"
|
||||||
|
|
||||||
def post(self, first_ci_id, second_ci_id):
|
def post(self, first_ci_id, second_ci_id):
|
||||||
|
ancestor_ids = request.values.get('ancestor_ids') or None
|
||||||
|
|
||||||
manager = CIRelationManager()
|
manager = CIRelationManager()
|
||||||
res = manager.add(first_ci_id, second_ci_id)
|
res = manager.add(first_ci_id, second_ci_id, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
return self.jsonify(cr_id=res)
|
return self.jsonify(cr_id=res)
|
||||||
|
|
||||||
def delete(self, first_ci_id, second_ci_id):
|
def delete(self, first_ci_id, second_ci_id):
|
||||||
|
ancestor_ids = request.values.get('ancestor_ids') or None
|
||||||
|
|
||||||
manager = CIRelationManager()
|
manager = CIRelationManager()
|
||||||
manager.delete_2(first_ci_id, second_ci_id)
|
manager.delete_2(first_ci_id, second_ci_id, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
return self.jsonify(message="CIType Relation is deleted")
|
return self.jsonify(message="CIType Relation is deleted")
|
||||||
|
|
||||||
|
@ -151,8 +157,9 @@ class BatchCreateOrUpdateCIRelationView(APIView):
|
||||||
ci_ids = list(map(int, request.values.get('ci_ids')))
|
ci_ids = list(map(int, request.values.get('ci_ids')))
|
||||||
parents = list(map(int, request.values.get('parents', [])))
|
parents = list(map(int, request.values.get('parents', [])))
|
||||||
children = list(map(int, request.values.get('children', [])))
|
children = list(map(int, request.values.get('children', [])))
|
||||||
|
ancestor_ids = request.values.get('ancestor_ids') or None
|
||||||
|
|
||||||
CIRelationManager.batch_update(ci_ids, parents, children)
|
CIRelationManager.batch_update(ci_ids, parents, children, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
|
||||||
|
@ -166,7 +173,8 @@ class BatchCreateOrUpdateCIRelationView(APIView):
|
||||||
def delete(self):
|
def delete(self):
|
||||||
ci_ids = list(map(int, request.values.get('ci_ids')))
|
ci_ids = list(map(int, request.values.get('ci_ids')))
|
||||||
parents = list(map(int, request.values.get('parents', [])))
|
parents = list(map(int, request.values.get('parents', [])))
|
||||||
|
ancestor_ids = request.values.get('ancestor_ids') or None
|
||||||
|
|
||||||
CIRelationManager.batch_delete(ci_ids, parents)
|
CIRelationManager.batch_delete(ci_ids, parents, ancestor_ids=ancestor_ids)
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
|
|
@ -9,6 +9,7 @@ from api.lib.cmdb.ci_type import CITypeRelationManager
|
||||||
from api.lib.cmdb.const import PermEnum
|
from api.lib.cmdb.const import PermEnum
|
||||||
from api.lib.cmdb.const import ResourceTypeEnum
|
from api.lib.cmdb.const import ResourceTypeEnum
|
||||||
from api.lib.cmdb.const import RoleEnum
|
from api.lib.cmdb.const import RoleEnum
|
||||||
|
from api.lib.cmdb.preference import PreferenceManager
|
||||||
from api.lib.cmdb.resp_format import ErrFormat
|
from api.lib.cmdb.resp_format import ErrFormat
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.perm.acl.acl import ACLManager
|
from api.lib.perm.acl.acl import ACLManager
|
||||||
|
@ -109,3 +110,10 @@ class CITypeRelationRevokeView(APIView):
|
||||||
acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
|
acl.revoke_resource_from_role_by_rid(resource_name, rid, ResourceTypeEnum.CI_TYPE_RELATION, perms)
|
||||||
|
|
||||||
return self.jsonify(code=200)
|
return self.jsonify(code=200)
|
||||||
|
|
||||||
|
|
||||||
|
class CITypeRelationCanEditView(APIView):
|
||||||
|
url_prefix = "/ci_type_relations/<int:parent_id>/<int:child_id>/can_edit"
|
||||||
|
|
||||||
|
def get(self, parent_id, child_id):
|
||||||
|
return self.jsonify(result=PreferenceManager.can_edit_relation(parent_id, child_id))
|
||||||
|
|
Loading…
Reference in New Issue