mirror of
https://github.com/veops/cmdb.git
synced 2025-08-25 04:41:45 +08:00
feat(api): multi-value attribute handling in relation building
This commit is contained in:
@@ -360,16 +360,28 @@ class CIManager(object):
|
|||||||
_sync=False,
|
_sync=False,
|
||||||
**ci_dict):
|
**ci_dict):
|
||||||
"""
|
"""
|
||||||
add ci
|
Create a new Configuration Item (CI) or update existing based on unique constraints.
|
||||||
:param ci_type_name:
|
|
||||||
:param exist_policy: replace or reject or need
|
Handles complete CI creation workflow including validation, uniqueness checks,
|
||||||
:param _no_attribute_policy: ignore or reject
|
password encryption, computed attributes, relationship creation, and caching.
|
||||||
:param is_auto_discovery: default is False
|
|
||||||
:param _is_admin: default is False
|
Args:
|
||||||
:param ticket_id:
|
ci_type_name (str): Name of the CI type to create
|
||||||
:param _sync:
|
exist_policy (ExistPolicy): How to handle existing CIs (REPLACE/REJECT/NEED)
|
||||||
:param ci_dict:
|
_no_attribute_policy (ExistPolicy): How to handle unknown attributes (IGNORE/REJECT)
|
||||||
:return:
|
is_auto_discovery (bool): Whether CI is created by auto-discovery process
|
||||||
|
_is_admin (bool): Whether to skip permission checks
|
||||||
|
ticket_id (int, optional): Associated ticket ID for audit trail
|
||||||
|
_sync (bool): Whether to execute cache/relation tasks synchronously
|
||||||
|
**ci_dict: CI attribute values as key-value pairs
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int: ID of the created or updated CI
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
400: If unique constraints violated, required attributes missing, or validation fails
|
||||||
|
403: If user lacks permissions for restricted attributes
|
||||||
|
404: If CI type not found or referenced CI not exists
|
||||||
"""
|
"""
|
||||||
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
ci_type = CITypeManager.check_is_existed(ci_type_name)
|
ci_type = CITypeManager.check_is_existed(ci_type_name)
|
||||||
@@ -512,6 +524,24 @@ class CIManager(object):
|
|||||||
return ci.id
|
return ci.id
|
||||||
|
|
||||||
def update(self, ci_id, _is_admin=False, ticket_id=None, _sync=False, **ci_dict):
|
def update(self, ci_id, _is_admin=False, ticket_id=None, _sync=False, **ci_dict):
|
||||||
|
"""
|
||||||
|
Update an existing Configuration Item with new attribute values.
|
||||||
|
|
||||||
|
Performs comprehensive CI update including validation, constraint checks,
|
||||||
|
password handling, computed attributes processing, and change tracking.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ci_id (int): ID of the CI to update
|
||||||
|
_is_admin (bool): Whether to skip permission checks
|
||||||
|
ticket_id (int, optional): Associated ticket ID for audit trail
|
||||||
|
_sync (bool): Whether to execute cache/relation tasks synchronously
|
||||||
|
**ci_dict: CI attribute values to update as key-value pairs
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
400: If unique constraints violated or validation fails
|
||||||
|
403: If user lacks permissions for restricted attributes
|
||||||
|
404: If CI not found
|
||||||
|
"""
|
||||||
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||||
ci = self.confirm_ci_existed(ci_id)
|
ci = self.confirm_ci_existed(ci_id)
|
||||||
ci_type = ci.ci_type
|
ci_type = ci.ci_type
|
||||||
@@ -1420,14 +1450,31 @@ class CIRelationManager(object):
|
|||||||
parent_attr = AttributeCache.get(parent_attr_id)
|
parent_attr = AttributeCache.get(parent_attr_id)
|
||||||
child_attr = AttributeCache.get(child_attr_id)
|
child_attr = AttributeCache.get(child_attr_id)
|
||||||
attr_value = ci_dict.get(parent_attr.name)
|
attr_value = ci_dict.get(parent_attr.name)
|
||||||
|
if attr_value != 0 and not attr_value:
|
||||||
|
continue
|
||||||
value_table = TableMap(attr=child_attr).table
|
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(
|
attr_value_list = [attr_value] if not isinstance(attr_value, list) else attr_value
|
||||||
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.child_id):
|
|
||||||
_relations.add((ci_dict['_id'], child.ci_id))
|
matching_cis = value_table.get_by(
|
||||||
|
attr_id=child_attr.id,
|
||||||
|
only_query=True
|
||||||
|
).join(
|
||||||
|
CI, CI.id == value_table.ci_id
|
||||||
|
).filter(
|
||||||
|
CI.type_id == item.child_id,
|
||||||
|
value_table.value.in_(attr_value_list)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
for ci in matching_cis:
|
||||||
|
_relations.add((ci_dict['_id'], ci.ci_id))
|
||||||
|
|
||||||
if relations is None:
|
if relations is None:
|
||||||
relations = _relations
|
relations = _relations
|
||||||
else:
|
else:
|
||||||
relations &= _relations
|
if item.constraint == ConstraintEnum.Many2Many:
|
||||||
|
relations |= _relations
|
||||||
|
else:
|
||||||
|
relations &= _relations
|
||||||
|
|
||||||
cls.delete_relations_by_source(RelationSourceEnum.ATTRIBUTE_VALUES,
|
cls.delete_relations_by_source(RelationSourceEnum.ATTRIBUTE_VALUES,
|
||||||
first_ci_id=ci_dict['_id'],
|
first_ci_id=ci_dict['_id'],
|
||||||
@@ -1447,14 +1494,31 @@ class CIRelationManager(object):
|
|||||||
parent_attr = AttributeCache.get(parent_attr_id)
|
parent_attr = AttributeCache.get(parent_attr_id)
|
||||||
child_attr = AttributeCache.get(child_attr_id)
|
child_attr = AttributeCache.get(child_attr_id)
|
||||||
attr_value = ci_dict.get(child_attr.name)
|
attr_value = ci_dict.get(child_attr.name)
|
||||||
|
if attr_value != 0 and not attr_value:
|
||||||
|
continue
|
||||||
value_table = TableMap(attr=parent_attr).table
|
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(
|
attr_value_list = [attr_value] if not isinstance(attr_value, list) else attr_value
|
||||||
CI, CI.id == value_table.ci_id).filter(CI.type_id == item.parent_id):
|
|
||||||
_relations.add((parent.ci_id, ci_dict['_id']))
|
matching_cis = value_table.get_by(
|
||||||
|
attr_id=parent_attr.id,
|
||||||
|
only_query=True
|
||||||
|
).join(
|
||||||
|
CI, CI.id == value_table.ci_id
|
||||||
|
).filter(
|
||||||
|
CI.type_id == item.parent_id,
|
||||||
|
value_table.value.in_(attr_value_list)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
for ci in matching_cis:
|
||||||
|
_relations.add((ci.ci_id, ci_dict['_id']))
|
||||||
|
|
||||||
if relations is None:
|
if relations is None:
|
||||||
relations = _relations
|
relations = _relations
|
||||||
else:
|
else:
|
||||||
relations &= _relations
|
if item.constraint == ConstraintEnum.Many2Many:
|
||||||
|
relations |= _relations
|
||||||
|
else:
|
||||||
|
relations &= _relations
|
||||||
|
|
||||||
cls.delete_relations_by_source(RelationSourceEnum.ATTRIBUTE_VALUES,
|
cls.delete_relations_by_source(RelationSourceEnum.ATTRIBUTE_VALUES,
|
||||||
second_ci_id=ci_dict['_id'],
|
second_ci_id=ci_dict['_id'],
|
||||||
|
@@ -92,7 +92,8 @@ class IpAddressManager(object):
|
|||||||
else:
|
else:
|
||||||
return abort(400, ErrFormat.ipam_address_model_not_found)
|
return abort(400, ErrFormat.ipam_address_model_not_found)
|
||||||
|
|
||||||
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id), expire=10)):
|
with (redis_lock.Lock(rd.r, "IPAM_ASSIGN_ADDRESS_{}".format(subnet_id),
|
||||||
|
expire=60, auto_renewal=True)):
|
||||||
cis = self._get_cis(subnet_id, ips)
|
cis = self._get_cis(subnet_id, ips)
|
||||||
ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis}
|
ip2ci = {ci[IPAddressBuiltinAttributes.IP]: ci for ci in cis}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user