mirror of https://github.com/veops/cmdb.git
feat(api): attribute association supports multiple groups (#527)
This commit is contained in:
parent
65ef58dea9
commit
50134e6a0b
|
@ -1274,52 +1274,83 @@ class CIRelationManager(object):
|
||||||
def build_by_attribute(cls, ci_dict):
|
def build_by_attribute(cls, ci_dict):
|
||||||
type_id = ci_dict['_type']
|
type_id = ci_dict['_type']
|
||||||
child_items = CITypeRelation.get_by(parent_id=type_id, only_query=True).filter(
|
child_items = CITypeRelation.get_by(parent_id=type_id, only_query=True).filter(
|
||||||
CITypeRelation.parent_attr_id.isnot(None))
|
CITypeRelation.parent_attr_ids.isnot(None))
|
||||||
for item in child_items:
|
for item in child_items:
|
||||||
parent_attr = AttributeCache.get(item.parent_attr_id)
|
relations = None
|
||||||
child_attr = AttributeCache.get(item.child_attr_id)
|
for parent_attr_id, child_attr_id in zip(item.parent_attr_ids, item.child_attr_ids):
|
||||||
attr_value = ci_dict.get(parent_attr.name)
|
_relations = set()
|
||||||
value_table = TableMap(attr=child_attr).table
|
parent_attr = AttributeCache.get(parent_attr_id)
|
||||||
for child in value_table.get_by(attr_id=child_attr.id, value=attr_value, only_query=True).join(
|
child_attr = AttributeCache.get(child_attr_id)
|
||||||
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.child_id):
|
attr_value = ci_dict.get(parent_attr.name)
|
||||||
CIRelationManager.add(ci_dict['_id'], child.ci_id, valid=False)
|
value_table = TableMap(attr=child_attr).table
|
||||||
|
for child in value_table.get_by(attr_id=child_attr.id, value=attr_value, only_query=True).join(
|
||||||
|
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.child_id):
|
||||||
|
_relations.add((ci_dict['_id'], child.ci_id))
|
||||||
|
if relations is None:
|
||||||
|
relations = _relations
|
||||||
|
else:
|
||||||
|
relations &= _relations
|
||||||
|
for parent_ci_id, child_ci_id in relations:
|
||||||
|
CIRelationManager.add(parent_ci_id, child_ci_id, valid=False)
|
||||||
|
|
||||||
parent_items = CITypeRelation.get_by(child_id=type_id, only_query=True).filter(
|
parent_items = CITypeRelation.get_by(child_id=type_id, only_query=True).filter(
|
||||||
CITypeRelation.child_attr_id.isnot(None))
|
CITypeRelation.child_attr_ids.isnot(None))
|
||||||
for item in parent_items:
|
for item in parent_items:
|
||||||
parent_attr = AttributeCache.get(item.parent_attr_id)
|
relations = None
|
||||||
child_attr = AttributeCache.get(item.child_attr_id)
|
for parent_attr_id, child_attr_id in zip(item.parent_attr_ids, item.child_attr_ids):
|
||||||
attr_value = ci_dict.get(child_attr.name)
|
_relations = set()
|
||||||
value_table = TableMap(attr=parent_attr).table
|
parent_attr = AttributeCache.get(parent_attr_id)
|
||||||
for parent in value_table.get_by(attr_id=parent_attr.id, value=attr_value, only_query=True).join(
|
child_attr = AttributeCache.get(child_attr_id)
|
||||||
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.parent_id):
|
attr_value = ci_dict.get(child_attr.name)
|
||||||
CIRelationManager.add(parent.ci_id, ci_dict['_id'], valid=False)
|
value_table = TableMap(attr=parent_attr).table
|
||||||
|
for parent in value_table.get_by(attr_id=parent_attr.id, value=attr_value, only_query=True).join(
|
||||||
|
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.parent_id):
|
||||||
|
_relations.add((parent.ci_id, ci_dict['_id']))
|
||||||
|
if relations is None:
|
||||||
|
relations = _relations
|
||||||
|
else:
|
||||||
|
relations &= _relations
|
||||||
|
for parent_ci_id, child_ci_id in relations:
|
||||||
|
CIRelationManager.add(parent_ci_id, child_ci_id, valid=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def rebuild_all_by_attribute(cls, ci_type_relation):
|
def rebuild_all_by_attribute(cls, ci_type_relation):
|
||||||
parent_attr = AttributeCache.get(ci_type_relation['parent_attr_id'])
|
relations = None
|
||||||
child_attr = AttributeCache.get(ci_type_relation['child_attr_id'])
|
for parent_attr_id, child_attr_id in zip(ci_type_relation['parent_attr_ids'] or [],
|
||||||
if not parent_attr or not child_attr:
|
ci_type_relation['child_attr_ids'] or []):
|
||||||
return
|
|
||||||
|
|
||||||
parent_value_table = TableMap(attr=parent_attr).table
|
_relations = set()
|
||||||
child_value_table = TableMap(attr=child_attr).table
|
parent_attr = AttributeCache.get(parent_attr_id)
|
||||||
|
child_attr = AttributeCache.get(child_attr_id)
|
||||||
|
if not parent_attr or not child_attr:
|
||||||
|
continue
|
||||||
|
|
||||||
parent_values = parent_value_table.get_by(attr_id=parent_attr.id, only_query=True).join(
|
parent_value_table = TableMap(attr=parent_attr).table
|
||||||
CI, CI.id == parent_value_table.ci_id).filter(CI.type_id == ci_type_relation['parent_id'])
|
child_value_table = TableMap(attr=child_attr).table
|
||||||
child_values = child_value_table.get_by(attr_id=child_attr.id, only_query=True).join(
|
|
||||||
CI, CI.id == child_value_table.ci_id).filter(CI.type_id == ci_type_relation['child_id'])
|
|
||||||
|
|
||||||
child_value2ci_ids = {}
|
parent_values = parent_value_table.get_by(attr_id=parent_attr.id, only_query=True).join(
|
||||||
for child in child_values:
|
CI, CI.id == parent_value_table.ci_id).filter(CI.type_id == ci_type_relation['parent_id'])
|
||||||
child_value2ci_ids.setdefault(child.value, []).append(child.ci_id)
|
child_values = child_value_table.get_by(attr_id=child_attr.id, only_query=True).join(
|
||||||
|
CI, CI.id == child_value_table.ci_id).filter(CI.type_id == ci_type_relation['child_id'])
|
||||||
|
|
||||||
for parent in parent_values:
|
child_value2ci_ids = {}
|
||||||
for child_ci_id in child_value2ci_ids.get(parent.value, []):
|
for child in child_values:
|
||||||
try:
|
child_value2ci_ids.setdefault(child.value, []).append(child.ci_id)
|
||||||
cls.add(parent.ci_id, child_ci_id, valid=False)
|
|
||||||
except:
|
for parent in parent_values:
|
||||||
pass
|
for child_ci_id in child_value2ci_ids.get(parent.value, []):
|
||||||
|
_relations.add((parent.ci_id, child_ci_id))
|
||||||
|
|
||||||
|
if relations is None:
|
||||||
|
relations = _relations
|
||||||
|
else:
|
||||||
|
relations &= _relations
|
||||||
|
|
||||||
|
for parent_ci_id, child_ci_id in (relations or []):
|
||||||
|
try:
|
||||||
|
cls.add(parent_ci_id, child_ci_id, valid=False)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CITriggerManager(object):
|
class CITriggerManager(object):
|
||||||
|
|
|
@ -35,6 +35,7 @@ from api.lib.perm.acl.acl import validate_permission
|
||||||
from api.models.cmdb import Attribute
|
from api.models.cmdb import Attribute
|
||||||
from api.models.cmdb import AutoDiscoveryCI
|
from api.models.cmdb import AutoDiscoveryCI
|
||||||
from api.models.cmdb import AutoDiscoveryCIType
|
from api.models.cmdb import AutoDiscoveryCIType
|
||||||
|
from api.models.cmdb import AutoDiscoveryCITypeRelation
|
||||||
from api.models.cmdb import CI
|
from api.models.cmdb import CI
|
||||||
from api.models.cmdb import CIFilterPerms
|
from api.models.cmdb import CIFilterPerms
|
||||||
from api.models.cmdb import CIType
|
from api.models.cmdb import CIType
|
||||||
|
@ -49,12 +50,12 @@ from api.models.cmdb import CITypeTrigger
|
||||||
from api.models.cmdb import CITypeUniqueConstraint
|
from api.models.cmdb import CITypeUniqueConstraint
|
||||||
from api.models.cmdb import CustomDashboard
|
from api.models.cmdb import CustomDashboard
|
||||||
from api.models.cmdb import PreferenceCITypeOrder
|
from api.models.cmdb import PreferenceCITypeOrder
|
||||||
from api.models.cmdb import TopologyView
|
|
||||||
from api.models.cmdb import PreferenceRelationView
|
from api.models.cmdb import PreferenceRelationView
|
||||||
from api.models.cmdb import PreferenceSearchOption
|
from api.models.cmdb import PreferenceSearchOption
|
||||||
from api.models.cmdb import PreferenceShowAttributes
|
from api.models.cmdb import PreferenceShowAttributes
|
||||||
from api.models.cmdb import PreferenceTreeView
|
from api.models.cmdb import PreferenceTreeView
|
||||||
from api.models.cmdb import RelationType
|
from api.models.cmdb import RelationType
|
||||||
|
from api.models.cmdb import TopologyView
|
||||||
|
|
||||||
|
|
||||||
class CITypeManager(object):
|
class CITypeManager(object):
|
||||||
|
@ -251,6 +252,12 @@ class CITypeManager(object):
|
||||||
for item in AutoDiscoveryCI.get_by(type_id=type_id, to_dict=False):
|
for item in AutoDiscoveryCI.get_by(type_id=type_id, to_dict=False):
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
|
for item in AutoDiscoveryCITypeRelation.get_by(ad_type_id=type_id, to_dict=False):
|
||||||
|
item.delete(commit=False)
|
||||||
|
|
||||||
|
for item in AutoDiscoveryCITypeRelation.get_by(peer_type_id=type_id, to_dict=False):
|
||||||
|
item.delete(commit=False)
|
||||||
|
|
||||||
for item in CITypeInheritance.get_by(parent_id=type_id, to_dict=False):
|
for item in CITypeInheritance.get_by(parent_id=type_id, to_dict=False):
|
||||||
item.delete(commit=False)
|
item.delete(commit=False)
|
||||||
|
|
||||||
|
@ -686,9 +693,19 @@ class CITypeAttributeManager(object):
|
||||||
item = CITypeTrigger.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False, first=True)
|
item = CITypeTrigger.get_by(type_id=_type_id, attr_id=attr_id, to_dict=False, first=True)
|
||||||
item and item.soft_delete(commit=False)
|
item and item.soft_delete(commit=False)
|
||||||
|
|
||||||
for item in (CITypeRelation.get_by(parent_id=type_id, parent_attr_id=attr_id, to_dict=False) +
|
for item in (CITypeRelation.get_by(parent_id=type_id, to_dict=False) +
|
||||||
CITypeRelation.get_by(child_id=type_id, child_attr_id=attr_id, to_dict=False)):
|
CITypeRelation.get_by(child_id=type_id, to_dict=False)):
|
||||||
item.soft_delete(commit=False)
|
if item.parent_id == type_id and attr_id in (item.parent_attr_ids or []):
|
||||||
|
item_dict = item.to_dict()
|
||||||
|
pop_idx = item.parent_attr_ids.index(attr_id)
|
||||||
|
elif item.child_id == type_id and attr_id in (item.child_attr_ids or []):
|
||||||
|
item_dict = item.to_dict()
|
||||||
|
pop_idx = item.child_attr_ids.index(attr_id)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
item.update(parent_attr_ids=item_dict['parent_attr_ids'].pop(pop_idx),
|
||||||
|
child_attr_ids=item_dict['child_attr_ids'].pop(pop_idx),
|
||||||
|
commit=False)
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -757,10 +774,12 @@ class CITypeRelationManager(object):
|
||||||
res[idx] = _item
|
res[idx] = _item
|
||||||
res[idx]['parent'] = item.parent.to_dict()
|
res[idx]['parent'] = item.parent.to_dict()
|
||||||
if item.parent_id not in type2attributes:
|
if item.parent_id not in type2attributes:
|
||||||
type2attributes[item.parent_id] = [i[1].to_dict() for i in CITypeAttributesCache.get2(item.parent_id)]
|
type2attributes[item.parent_id] = [i[1].to_dict() for i in
|
||||||
|
CITypeAttributeManager.get_all_attributes(item.parent_id)]
|
||||||
res[idx]['child'] = item.child.to_dict()
|
res[idx]['child'] = item.child.to_dict()
|
||||||
if item.child_id not in type2attributes:
|
if item.child_id not in type2attributes:
|
||||||
type2attributes[item.child_id] = [i[1].to_dict() for i in CITypeAttributesCache.get2(item.child_id)]
|
type2attributes[item.child_id] = [i[1].to_dict() for i in
|
||||||
|
CITypeAttributeManager.get_all_attributes(item.child_id)]
|
||||||
res[idx]['relation_type'] = item.relation_type.to_dict()
|
res[idx]['relation_type'] = item.relation_type.to_dict()
|
||||||
|
|
||||||
return res, type2attributes
|
return res, type2attributes
|
||||||
|
@ -795,8 +814,8 @@ class CITypeRelationManager(object):
|
||||||
|
|
||||||
ci_type_dict["relation_type"] = relation_inst.relation_type.name
|
ci_type_dict["relation_type"] = relation_inst.relation_type.name
|
||||||
ci_type_dict["constraint"] = relation_inst.constraint
|
ci_type_dict["constraint"] = relation_inst.constraint
|
||||||
ci_type_dict["parent_attr_id"] = relation_inst.parent_attr_id
|
ci_type_dict["parent_attr_ids"] = relation_inst.parent_attr_ids
|
||||||
ci_type_dict["child_attr_id"] = relation_inst.child_attr_id
|
ci_type_dict["child_attr_ids"] = relation_inst.child_attr_ids
|
||||||
|
|
||||||
return ci_type_dict
|
return ci_type_dict
|
||||||
|
|
||||||
|
@ -906,7 +925,7 @@ class CITypeRelationManager(object):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add(cls, parent, child, relation_type_id, constraint=ConstraintEnum.One2Many,
|
def add(cls, parent, child, relation_type_id, constraint=ConstraintEnum.One2Many,
|
||||||
parent_attr_id=None, child_attr_id=None):
|
parent_attr_ids=None, child_attr_ids=None):
|
||||||
p = CITypeManager.check_is_existed(parent)
|
p = CITypeManager.check_is_existed(parent)
|
||||||
c = CITypeManager.check_is_existed(child)
|
c = CITypeManager.check_is_existed(child)
|
||||||
|
|
||||||
|
@ -921,21 +940,22 @@ 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)
|
||||||
|
|
||||||
old_parent_attr_id = None
|
old_parent_attr_ids, old_child_attr_ids = None, None
|
||||||
existed = cls._get(p.id, c.id)
|
existed = cls._get(p.id, c.id)
|
||||||
if existed is not None:
|
if existed is not None:
|
||||||
old_parent_attr_id = existed.parent_attr_id
|
old_parent_attr_ids = copy.deepcopy(existed.parent_attr_ids)
|
||||||
|
old_child_attr_ids = copy.deepcopy(existed.child_attr_ids)
|
||||||
existed = existed.update(relation_type_id=relation_type_id,
|
existed = existed.update(relation_type_id=relation_type_id,
|
||||||
constraint=constraint,
|
constraint=constraint,
|
||||||
parent_attr_id=parent_attr_id,
|
parent_attr_ids=parent_attr_ids,
|
||||||
child_attr_id=child_attr_id,
|
child_attr_ids=child_attr_ids,
|
||||||
filter_none=False)
|
filter_none=False)
|
||||||
else:
|
else:
|
||||||
existed = CITypeRelation.create(parent_id=p.id,
|
existed = CITypeRelation.create(parent_id=p.id,
|
||||||
child_id=c.id,
|
child_id=c.id,
|
||||||
relation_type_id=relation_type_id,
|
relation_type_id=relation_type_id,
|
||||||
parent_attr_id=parent_attr_id,
|
parent_attr_ids=parent_attr_ids,
|
||||||
child_attr_id=child_attr_id,
|
child_attr_ids=child_attr_ids,
|
||||||
constraint=constraint)
|
constraint=constraint)
|
||||||
|
|
||||||
if current_app.config.get("USE_ACL"):
|
if current_app.config.get("USE_ACL"):
|
||||||
|
@ -949,10 +969,10 @@ class CITypeRelationManager(object):
|
||||||
current_user.username,
|
current_user.username,
|
||||||
ResourceTypeEnum.CI_TYPE_RELATION)
|
ResourceTypeEnum.CI_TYPE_RELATION)
|
||||||
|
|
||||||
if parent_attr_id and parent_attr_id != old_parent_attr_id:
|
if ((parent_attr_ids and parent_attr_ids != old_parent_attr_ids) or
|
||||||
if parent_attr_id and parent_attr_id != existed.parent_attr_id:
|
(child_attr_ids and child_attr_ids != old_child_attr_ids)):
|
||||||
from api.tasks.cmdb import rebuild_relation_for_attribute_changed
|
from api.tasks.cmdb import rebuild_relation_for_attribute_changed
|
||||||
rebuild_relation_for_attribute_changed.apply_async(args=(existed.to_dict()))
|
rebuild_relation_for_attribute_changed.apply_async(args=(existed.to_dict(),))
|
||||||
|
|
||||||
CITypeHistoryManager.add(CITypeOperateType.ADD_RELATION, p.id,
|
CITypeHistoryManager.add(CITypeOperateType.ADD_RELATION, p.id,
|
||||||
change=dict(parent=p.to_dict(), child=c.to_dict(), relation_type_id=relation_type_id))
|
change=dict(parent=p.to_dict(), child=c.to_dict(), relation_type_id=relation_type_id))
|
||||||
|
@ -1246,8 +1266,8 @@ class CITypeTemplateManager(object):
|
||||||
id2obj_dicts[added_id].get('child_id'),
|
id2obj_dicts[added_id].get('child_id'),
|
||||||
id2obj_dicts[added_id].get('relation_type_id'),
|
id2obj_dicts[added_id].get('relation_type_id'),
|
||||||
id2obj_dicts[added_id].get('constraint'),
|
id2obj_dicts[added_id].get('constraint'),
|
||||||
id2obj_dicts[added_id].get('parent_attr_id'),
|
id2obj_dicts[added_id].get('parent_attr_ids'),
|
||||||
id2obj_dicts[added_id].get('child_attr_id'),
|
id2obj_dicts[added_id].get('child_attr_ids'),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
obj = cls.create(flush=True, **id2obj_dicts[added_id])
|
obj = cls.create(flush=True, **id2obj_dicts[added_id])
|
||||||
|
|
|
@ -79,8 +79,11 @@ class CITypeRelation(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)
|
||||||
constraint = db.Column(db.Enum(*ConstraintEnum.all()), default=ConstraintEnum.One2Many)
|
constraint = db.Column(db.Enum(*ConstraintEnum.all()), default=ConstraintEnum.One2Many)
|
||||||
|
|
||||||
parent_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
|
parent_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id")) # CMDB > 2.4.5: deprecated
|
||||||
child_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id"))
|
child_attr_id = db.Column(db.Integer, db.ForeignKey("c_attributes.id")) # CMDB > 2.4.5: deprecated
|
||||||
|
|
||||||
|
parent_attr_ids = db.Column(db.JSON) # [parent_attr_id, ]
|
||||||
|
child_attr_ids = db.Column(db.JSON) # [child_attr_id, ]
|
||||||
|
|
||||||
parent = db.relationship("CIType", primaryjoin="CIType.id==CITypeRelation.parent_id")
|
parent = db.relationship("CIType", primaryjoin="CIType.id==CITypeRelation.parent_id")
|
||||||
child = db.relationship("CIType", primaryjoin="CIType.id==CITypeRelation.child_id")
|
child = db.relationship("CIType", primaryjoin="CIType.id==CITypeRelation.child_id")
|
||||||
|
|
|
@ -57,10 +57,10 @@ class CITypeRelationView(APIView):
|
||||||
def post(self, parent_id, child_id):
|
def post(self, parent_id, child_id):
|
||||||
relation_type_id = request.values.get("relation_type_id")
|
relation_type_id = request.values.get("relation_type_id")
|
||||||
constraint = request.values.get("constraint")
|
constraint = request.values.get("constraint")
|
||||||
parent_attr_id = request.values.get("parent_attr_id")
|
parent_attr_ids = request.values.get("parent_attr_ids")
|
||||||
child_attr_id = request.values.get("child_attr_id")
|
child_attr_ids = request.values.get("child_attr_ids")
|
||||||
ctr_id = CITypeRelationManager.add(parent_id, child_id, relation_type_id, constraint,
|
ctr_id = CITypeRelationManager.add(parent_id, child_id, relation_type_id, constraint,
|
||||||
parent_attr_id, child_attr_id)
|
parent_attr_ids, child_attr_ids)
|
||||||
|
|
||||||
return self.jsonify(ctr_id=ctr_id)
|
return self.jsonify(ctr_id=ctr_id)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue