Merge pull request #593 from veops/dev_api_0820

feat(api): supports bool and reference
This commit is contained in:
pycook 2024-08-20 13:51:44 +08:00 committed by GitHub
commit 40a4db06b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 242 additions and 107 deletions

View File

@ -167,24 +167,30 @@ class AttributeManager(object):
def get_attribute_by_name(self, name): def get_attribute_by_name(self, name):
attr = Attribute.get_by(name=name, first=True) attr = Attribute.get_by(name=name, first=True)
if attr.get("is_choice"): if attr.get("is_choice"):
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], attr["choice_value"] = self.get_choice_values(attr["id"],
attr["choice_web_hook"], attr.get("choice_other")) attr["value_type"],
attr["choice_web_hook"],
attr.get("choice_other"))
return attr return attr
def get_attribute_by_alias(self, alias): def get_attribute_by_alias(self, alias):
attr = Attribute.get_by(alias=alias, first=True) attr = Attribute.get_by(alias=alias, first=True)
if attr.get("is_choice"): if attr.get("is_choice"):
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], attr["choice_value"] = self.get_choice_values(attr["id"],
attr["choice_web_hook"], attr.get("choice_other")) attr["value_type"],
attr["choice_web_hook"],
attr.get("choice_other"))
return attr return attr
def get_attribute_by_id(self, _id): def get_attribute_by_id(self, _id):
attr = Attribute.get_by_id(_id).to_dict() attr = Attribute.get_by_id(_id).to_dict()
if attr.get("is_choice"): if attr.get("is_choice"):
attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], attr["choice_value"] = self.get_choice_values(attr["id"],
attr["choice_web_hook"], attr.get("choice_other")) attr["value_type"],
attr["choice_web_hook"],
attr.get("choice_other"))
return attr return attr

View File

@ -266,7 +266,7 @@ class CIManager(object):
value_table = TableMap(attr_name=id2name[attr_id]).table value_table = TableMap(attr_name=id2name[attr_id]).table
values = value_table.get_by(attr_id=attr_id, values = value_table.get_by(attr_id=attr_id,
value=ci_dict.get(id2name[attr_id]) or None, value=ci_dict.get(id2name[attr_id]),
only_query=True).join( only_query=True).join(
CI, CI.id == value_table.ci_id).filter(CI.type_id == type_id) CI, CI.id == value_table.ci_id).filter(CI.type_id == type_id)
_ci_ids = set([i.ci_id for i in values]) _ci_ids = set([i.ci_id for i in values])
@ -292,6 +292,53 @@ class CIManager(object):
return 1 return 1
@staticmethod
def _reference_to_ci_id(attr, payload):
def __unique_value2id(_type, _v):
value_table = TableMap(attr_name=_type.unique_id).table
ci = value_table.get_by(attr_id=attr.id, value=_v)
if ci is not None:
return ci.ci_id
return abort(400, ErrFormat.ci_reference_invalid.format(attr.alias, _v))
def __valid_reference_id_existed(_id, _type_id):
ci = CI.get_by_id(_id) or abort(404, ErrFormat.ci_reference_not_found.format(attr.alias, _id))
if ci.type_id != _type_id:
return abort(400, ErrFormat.ci_reference_invalid.format(attr.alias, _id))
if attr.name in payload:
k, reference_value = attr.name, payload[attr.name]
elif attr.alias in payload:
k, reference_value = attr.alias, payload[attr.alias]
else:
return
if not reference_value:
return
reference_type = None
if isinstance(reference_value, list):
for idx, v in enumerate(reference_value):
if isinstance(v, dict) and v.get('unique'):
if reference_type is None:
reference_type = CITypeCache.get(attr.reference_type_id)
if reference_type is not None:
reference_value[idx] = __unique_value2id(reference_type, v)
else:
__valid_reference_id_existed(v, attr.reference_type_id)
elif isinstance(reference_value, dict) and reference_value.get('unique'):
if reference_type is None:
reference_type = CITypeCache.get(attr.reference_type_id)
if reference_type is not None:
reference_value = __unique_value2id(reference_type, reference_value)
elif str(reference_value).isdigit():
reference_value = int(reference_value)
__valid_reference_id_existed(reference_value, attr.reference_type_id)
payload[k] = reference_value
@classmethod @classmethod
def add(cls, ci_type_name, def add(cls, ci_type_name,
exist_policy=ExistPolicy.REPLACE, exist_policy=ExistPolicy.REPLACE,
@ -392,6 +439,8 @@ class CIManager(object):
if attr.re_check and password_dict.get(attr.id): if attr.re_check and password_dict.get(attr.id):
value_manager.check_re(attr.re_check, attr.alias, password_dict[attr.id][0]) value_manager.check_re(attr.re_check, attr.alias, password_dict[attr.id][0])
elif attr.is_reference:
cls._reference_to_ci_id(attr, ci_dict)
cls._valid_unique_constraint(ci_type.id, ci_dict, ci and ci.id) cls._valid_unique_constraint(ci_type.id, ci_dict, ci and ci.id)
@ -443,9 +492,10 @@ 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):
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
attrs = CITypeAttributeManager.get_all_attributes(ci.type_id) attrs = CITypeAttributeManager.get_all_attributes(ci.type_id)
ci_type_attrs_name = {attr.name: attr for _, attr in attrs} ci_type_attrs_name = {attr.name: attr for _, attr in attrs}
@ -475,11 +525,13 @@ class CIManager(object):
if attr.re_check and password_dict.get(attr.id): if attr.re_check and password_dict.get(attr.id):
value_manager.check_re(attr.re_check, attr.alias, password_dict[attr.id][0]) value_manager.check_re(attr.re_check, attr.alias, password_dict[attr.id][0])
elif attr.is_reference:
self._reference_to_ci_id(attr, ci_dict)
limit_attrs = self._valid_ci_for_no_read(ci) if not _is_admin else {} limit_attrs = self._valid_ci_for_no_read(ci) if not _is_admin else {}
record_id = None record_id = None
with redis_lock.Lock(rd.r, ci.ci_type.name): with redis_lock.Lock(rd.r, ci_type.name):
db.session.commit() db.session.commit()
self._valid_unique_constraint(ci.type_id, ci_dict, ci_id) self._valid_unique_constraint(ci.type_id, ci_dict, ci_id)
@ -510,14 +562,14 @@ class CIManager(object):
record_id = self.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci.type_id) record_id = self.save_password(ci.id, attr_id, password_dict[attr_id], record_id, ci.type_id)
if record_id or has_dynamic: # has changed if record_id or has_dynamic: # has changed
if not __sync: if not _sync:
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE) ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE)
else: else:
ci_cache(ci_id, OperateType.UPDATE, record_id) ci_cache(ci_id, OperateType.UPDATE, record_id)
ref_ci_dict = {k: v for k, v in ci_dict.items() if k.startswith("$") and "." in k} ref_ci_dict = {k: v for k, v in ci_dict.items() if k.startswith("$") and "." in k}
if ref_ci_dict: if ref_ci_dict:
if not __sync: if not _sync:
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id), queue=CMDB_QUEUE) ci_relation_add.apply_async(args=(ref_ci_dict, ci.id), queue=CMDB_QUEUE)
else: else:
ci_relation_add(ref_ci_dict, ci.id) ci_relation_add(ref_ci_dict, ci.id)
@ -578,7 +630,7 @@ class CIManager(object):
if ci_dict: if ci_dict:
AttributeHistoryManger.add(None, ci_id, [(None, OperateType.DELETE, ci_dict, None)], ci.type_id) AttributeHistoryManger.add(None, ci_id, [(None, OperateType.DELETE, ci_dict, None)], ci.type_id)
ci_delete.apply_async(args=(ci_id,), queue=CMDB_QUEUE) ci_delete.apply_async(args=(ci_id, ci.type_id), queue=CMDB_QUEUE)
delete_id_filter.apply_async(args=(ci_id,), queue=CMDB_QUEUE) delete_id_filter.apply_async(args=(ci_id,), queue=CMDB_QUEUE)
return ci_id return ci_id
@ -773,7 +825,7 @@ class CIManager(object):
value_table = ValueTypeMap.table[ValueTypeEnum.PASSWORD] value_table = ValueTypeMap.table[ValueTypeEnum.PASSWORD]
if current_app.config.get('SECRETS_ENGINE') == 'inner': if current_app.config.get('SECRETS_ENGINE') == 'inner':
if value: if value:
encrypt_value, status = InnerCrypt().encrypt(value) encrypt_value, status = InnerCrypt().encrypt(str(value))
if not status: if not status:
current_app.logger.error('save password failed: {}'.format(encrypt_value)) current_app.logger.error('save password failed: {}'.format(encrypt_value))
return abort(400, ErrFormat.password_save_failed.format(encrypt_value)) return abort(400, ErrFormat.password_save_failed.format(encrypt_value))
@ -801,7 +853,7 @@ class CIManager(object):
vault = VaultClient(current_app.config.get('VAULT_URL'), current_app.config.get('VAULT_TOKEN')) vault = VaultClient(current_app.config.get('VAULT_URL'), current_app.config.get('VAULT_TOKEN'))
if value: if value:
try: try:
vault.update("/{}/{}".format(ci_id, attr_id), dict(v=value)) vault.update("/{}/{}".format(ci_id, attr_id), dict(v=str(value)))
except Exception as e: except Exception as e:
current_app.logger.error('save password to vault failed: {}'.format(e)) current_app.logger.error('save password to vault failed: {}'.format(e))
return abort(400, ErrFormat.password_save_failed.format('write vault failed')) return abort(400, ErrFormat.password_save_failed.format('write vault failed'))

View File

@ -145,7 +145,7 @@ class CITypeManager(object):
kwargs["alias"] = kwargs["name"] if not kwargs.get("alias") else kwargs["alias"] kwargs["alias"] = kwargs["name"] if not kwargs.get("alias") else kwargs["alias"]
cls._validate_unique(name=kwargs['name']) cls._validate_unique(name=kwargs['name'])
cls._validate_unique(alias=kwargs['alias']) # cls._validate_unique(alias=kwargs['alias'])
kwargs["unique_id"] = unique_key.id kwargs["unique_id"] = unique_key.id
kwargs['uid'] = current_user.uid kwargs['uid'] = current_user.uid
@ -184,7 +184,7 @@ class CITypeManager(object):
ci_type = cls.check_is_existed(type_id) ci_type = cls.check_is_existed(type_id)
cls._validate_unique(type_id=type_id, name=kwargs.get('name')) cls._validate_unique(type_id=type_id, name=kwargs.get('name'))
cls._validate_unique(type_id=type_id, alias=kwargs.get('alias') or kwargs.get('name')) # cls._validate_unique(type_id=type_id, alias=kwargs.get('alias') or kwargs.get('name'))
unique_key = kwargs.pop("unique_key", None) unique_key = kwargs.pop("unique_key", None)
unique_key = AttributeCache.get(unique_key) unique_key = AttributeCache.get(unique_key)
@ -234,6 +234,10 @@ class CITypeManager(object):
if CITypeInheritance.get_by(parent_id=type_id, first=True): if CITypeInheritance.get_by(parent_id=type_id, first=True):
return abort(400, ErrFormat.ci_type_inheritance_cannot_delete) return abort(400, ErrFormat.ci_type_inheritance_cannot_delete)
reference = Attribute.get_by(reference_type_id=type_id, first=True, to_dict=False)
if reference is not None:
return abort(400, ErrFormat.ci_type_referenced_cannot_delete.format(reference.alias))
relation_views = PreferenceRelationView.get_by(to_dict=False) relation_views = PreferenceRelationView.get_by(to_dict=False)
for rv in relation_views: for rv in relation_views:
for item in (rv.cr_ids or []): for item in (rv.cr_ids or []):
@ -1323,6 +1327,7 @@ class CITypeTemplateManager(object):
def _import_attributes(self, type2attributes): def _import_attributes(self, type2attributes):
attributes = [attr for type_id in type2attributes for attr in type2attributes[type_id]] attributes = [attr for type_id in type2attributes for attr in type2attributes[type_id]]
attrs = [] attrs = []
references = []
for i in copy.deepcopy(attributes): for i in copy.deepcopy(attributes):
if i.pop('inherited', None): if i.pop('inherited', None):
continue continue
@ -1337,6 +1342,10 @@ class CITypeTemplateManager(object):
if not choice_value: if not choice_value:
i['is_choice'] = False i['is_choice'] = False
if i.get('reference_type_id'):
references.append(copy.deepcopy(i))
i.pop('reference_type_id')
attrs.append((i, choice_value)) attrs.append((i, choice_value))
attr_id_map = self.__import(Attribute, [i[0] for i in copy.deepcopy(attrs)]) attr_id_map = self.__import(Attribute, [i[0] for i in copy.deepcopy(attrs)])
@ -1345,7 +1354,7 @@ class CITypeTemplateManager(object):
if choice_value and not i.get('choice_web_hook') and not i.get('choice_other'): if choice_value and not i.get('choice_web_hook') and not i.get('choice_other'):
AttributeManager.add_choice_values(attr_id_map.get(i['id'], i['id']), i['value_type'], choice_value) AttributeManager.add_choice_values(attr_id_map.get(i['id'], i['id']), i['value_type'], choice_value)
return attr_id_map return attr_id_map, references
def _import_ci_types(self, ci_types, attr_id_map): def _import_ci_types(self, ci_types, attr_id_map):
for i in ci_types: for i in ci_types:
@ -1359,6 +1368,11 @@ class CITypeTemplateManager(object):
return self.__import(CIType, ci_types) return self.__import(CIType, ci_types)
def _import_reference_attributes(self, attrs, type_id_map):
for attr in attrs:
attr['reference_type_id'] = type_id_map.get(attr['reference_type_id'], attr['reference_type_id'])
self.__import(Attribute, attrs)
def _import_ci_type_groups(self, ci_type_groups, type_id_map): def _import_ci_type_groups(self, ci_type_groups, type_id_map):
_ci_type_groups = copy.deepcopy(ci_type_groups) _ci_type_groups = copy.deepcopy(ci_type_groups)
for i in _ci_type_groups: for i in _ci_type_groups:
@ -1584,13 +1598,15 @@ class CITypeTemplateManager(object):
import time import time
s = time.time() s = time.time()
attr_id_map = self._import_attributes(tpt.get('type2attributes') or {}) attr_id_map, references = self._import_attributes(tpt.get('type2attributes') or {})
current_app.logger.info('import attributes cost: {}'.format(time.time() - s)) current_app.logger.info('import attributes cost: {}'.format(time.time() - s))
s = time.time() s = time.time()
ci_type_id_map = self._import_ci_types(tpt.get('ci_types') or [], attr_id_map) ci_type_id_map = self._import_ci_types(tpt.get('ci_types') or [], attr_id_map)
current_app.logger.info('import ci_types cost: {}'.format(time.time() - s)) current_app.logger.info('import ci_types cost: {}'.format(time.time() - s))
self._import_reference_attributes(references, ci_type_id_map)
s = time.time() s = time.time()
self._import_ci_type_groups(tpt.get('ci_type_groups') or [], ci_type_id_map) self._import_ci_type_groups(tpt.get('ci_type_groups') or [], ci_type_id_map)
current_app.logger.info('import ci_type_groups cost: {}'.format(time.time() - s)) current_app.logger.info('import ci_type_groups cost: {}'.format(time.time() - s))
@ -1675,6 +1691,16 @@ class CITypeTemplateManager(object):
type_ids.extend(extend_type_ids) type_ids.extend(extend_type_ids)
ci_types.extend(CITypeManager.get_ci_types(type_ids=extend_type_ids)) ci_types.extend(CITypeManager.get_ci_types(type_ids=extend_type_ids))
# handle reference type
references = Attribute.get_by(only_query=True).join(
CITypeAttribute, CITypeAttribute.attr_id == Attribute.id).filter(
CITypeAttribute.type_id.in_(type_ids)).filter(CITypeAttribute.deleted.is_(False)).filter(
Attribute.reference_type_id.isnot(None))
reference_type_ids = list(set([i.reference_type_id for i in references if i.reference_type_id]))
if reference_type_ids:
type_ids.extend(reference_type_ids)
ci_types.extend(CITypeManager.get_ci_types(type_ids=reference_type_ids))
tpt = dict( tpt = dict(
ci_types=ci_types, ci_types=ci_types,
relation_types=[i.to_dict() for i in RelationTypeManager.get_all()], relation_types=[i.to_dict() for i in RelationTypeManager.get_all()],
@ -1687,6 +1713,7 @@ class CITypeTemplateManager(object):
icons=dict() icons=dict()
) )
tpt['ci_type_groups'] = CITypeGroupManager.get(ci_types=tpt['ci_types'], type_ids=type_ids) tpt['ci_type_groups'] = CITypeGroupManager.get(ci_types=tpt['ci_types'], type_ids=type_ids)
tpt['ci_type_groups'] = [i for i in tpt['ci_type_groups'] if i.get('ci_types')]
def get_icon_value(icon): def get_icon_value(icon):
try: try:

View File

@ -14,6 +14,8 @@ class ValueTypeEnum(BaseEnum):
JSON = "6" JSON = "6"
PASSWORD = TEXT PASSWORD = TEXT
LINK = TEXT LINK = TEXT
BOOL = "7"
REFERENCE = INT
class ConstraintEnum(BaseEnum): class ConstraintEnum(BaseEnum):

View File

@ -44,6 +44,8 @@ class ErrFormat(CommonErrFormat):
unique_value_not_found = _l("The model's primary key {} does not exist!") # 模型的主键 {} 不存在! unique_value_not_found = _l("The model's primary key {} does not exist!") # 模型的主键 {} 不存在!
unique_key_required = _l("Primary key {} is missing") # 主键字段 {} 缺失 unique_key_required = _l("Primary key {} is missing") # 主键字段 {} 缺失
ci_is_already_existed = _l("CI already exists!") # CI 已经存在! ci_is_already_existed = _l("CI already exists!") # CI 已经存在!
ci_reference_not_found = _l("{}: CI reference {} does not exist!") # {}: CI引用 {} 不存在!
ci_reference_invalid = _l("{}: CI reference {} is illegal!") # {}, CI引用 {} 不合法!
relation_constraint = _l("Relationship constraint: {}, verification failed") # 关系约束: {}, 校验失败 relation_constraint = _l("Relationship constraint: {}, verification failed") # 关系约束: {}, 校验失败
# 多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系! # 多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!
m2m_relation_constraint = _l( m2m_relation_constraint = _l(
@ -63,6 +65,8 @@ class ErrFormat(CommonErrFormat):
ci_exists_and_cannot_delete_inheritance = _l( ci_exists_and_cannot_delete_inheritance = _l(
"The inheritance cannot be deleted because the CI already exists") # 因为CI已经存在不能删除继承关系 "The inheritance cannot be deleted because the CI already exists") # 因为CI已经存在不能删除继承关系
ci_type_inheritance_cannot_delete = _l("The model is inherited and cannot be deleted") # 该模型被继承, 不能删除 ci_type_inheritance_cannot_delete = _l("The model is inherited and cannot be deleted") # 该模型被继承, 不能删除
ci_type_referenced_cannot_delete = _l(
"The model is referenced by attribute {} and cannot be deleted") # 该模型被属性 {} 引用, 不能删除
# 因为关系视图 {} 引用了该模型,不能删除模型 # 因为关系视图 {} 引用了该模型,不能删除模型
ci_relation_view_exists_and_cannot_delete_type = _l( ci_relation_view_exists_and_cannot_delete_type = _l(

View File

@ -451,6 +451,9 @@ class Search(object):
if field_type == ValueTypeEnum.DATE and len(v) == 10: if field_type == ValueTypeEnum.DATE and len(v) == 10:
v = "{} 00:00:00".format(v) v = "{} 00:00:00".format(v)
if field_type == ValueTypeEnum.BOOL and "*" not in str(v):
v = str(int(v in current_app.config.get('BOOL_TRUE')))
# in query # in query
if v.startswith("(") and v.endswith(")"): if v.startswith("(") and v.endswith(")"):
_query_sql = self._in_query_handler(attr, v, is_not) _query_sql = self._in_query_handler(attr, v, is_not)

View File

@ -7,6 +7,7 @@ import json
import re import re
import six import six
from flask import current_app
import api.models.cmdb as model import api.models.cmdb as model
from api.lib.cmdb.cache import AttributeCache from api.lib.cmdb.cache import AttributeCache
@ -64,6 +65,7 @@ class ValueTypeMap(object):
ValueTypeEnum.DATETIME: str2datetime, ValueTypeEnum.DATETIME: str2datetime,
ValueTypeEnum.DATE: str2date, ValueTypeEnum.DATE: str2date,
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x, ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x,
ValueTypeEnum.BOOL: lambda x: x in current_app.config.get('BOOL_TRUE'),
} }
serialize = { serialize = {
@ -74,6 +76,7 @@ class ValueTypeMap(object):
ValueTypeEnum.DATE: lambda x: x.strftime("%Y-%m-%d") if not isinstance(x, six.string_types) else x, ValueTypeEnum.DATE: lambda x: x.strftime("%Y-%m-%d") if not isinstance(x, six.string_types) else x,
ValueTypeEnum.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S") if not isinstance(x, six.string_types) else x, ValueTypeEnum.DATETIME: lambda x: x.strftime("%Y-%m-%d %H:%M:%S") if not isinstance(x, six.string_types) else x,
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x, ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x,
ValueTypeEnum.BOOL: lambda x: x in current_app.config.get('BOOL_TRUE'),
} }
serialize2 = { serialize2 = {
@ -84,6 +87,7 @@ class ValueTypeMap(object):
ValueTypeEnum.DATE: lambda x: (x.decode() if not isinstance(x, six.string_types) else x).split()[0], ValueTypeEnum.DATE: lambda x: (x.decode() if not isinstance(x, six.string_types) else x).split()[0],
ValueTypeEnum.DATETIME: lambda x: x.decode() if not isinstance(x, six.string_types) else x, ValueTypeEnum.DATETIME: lambda x: x.decode() if not isinstance(x, six.string_types) else x,
ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x, ValueTypeEnum.JSON: lambda x: json.loads(x) if isinstance(x, six.string_types) and x else x,
ValueTypeEnum.BOOL: lambda x: x in current_app.config.get('BOOL_TRUE'),
} }
choice = { choice = {
@ -105,6 +109,7 @@ class ValueTypeMap(object):
'index_{0}'.format(ValueTypeEnum.TIME): model.CIIndexValueText, 'index_{0}'.format(ValueTypeEnum.TIME): model.CIIndexValueText,
'index_{0}'.format(ValueTypeEnum.FLOAT): model.CIIndexValueFloat, 'index_{0}'.format(ValueTypeEnum.FLOAT): model.CIIndexValueFloat,
'index_{0}'.format(ValueTypeEnum.JSON): model.CIValueJson, 'index_{0}'.format(ValueTypeEnum.JSON): model.CIValueJson,
'index_{0}'.format(ValueTypeEnum.BOOL): model.CIIndexValueInteger,
} }
table_name = { table_name = {
@ -117,6 +122,7 @@ class ValueTypeMap(object):
'index_{0}'.format(ValueTypeEnum.TIME): 'c_value_index_texts', 'index_{0}'.format(ValueTypeEnum.TIME): 'c_value_index_texts',
'index_{0}'.format(ValueTypeEnum.FLOAT): 'c_value_index_floats', 'index_{0}'.format(ValueTypeEnum.FLOAT): 'c_value_index_floats',
'index_{0}'.format(ValueTypeEnum.JSON): 'c_value_json', 'index_{0}'.format(ValueTypeEnum.JSON): 'c_value_json',
'index_{0}'.format(ValueTypeEnum.BOOL): 'c_value_index_integers',
} }
es_type = { es_type = {

View File

@ -128,14 +128,20 @@ class AttributeValueManager(object):
return abort(400, ErrFormat.attribute_value_invalid2.format(alias, value)) return abort(400, ErrFormat.attribute_value_invalid2.format(alias, value))
def _validate(self, attr, value, value_table, ci=None, type_id=None, ci_id=None, type_attr=None): def _validate(self, attr, value, value_table, ci=None, type_id=None, ci_id=None, type_attr=None):
if not attr.is_reference:
ci = ci or {} ci = ci or {}
v = self._deserialize_value(attr.alias, attr.value_type, value) v = self._deserialize_value(attr.alias, attr.value_type, value)
attr.is_choice and value and self._check_is_choice(attr, attr.value_type, v) attr.is_choice and value and self._check_is_choice(attr, attr.value_type, v)
else:
v = value or None
attr.is_unique and self._check_is_unique( attr.is_unique and self._check_is_unique(
value_table, attr, ci and ci.id or ci_id, ci and ci.type_id or type_id, v) value_table, attr, ci and ci.id or ci_id, ci and ci.type_id or type_id, v)
self._check_is_required(ci and ci.type_id or type_id, attr, v, type_attr=type_attr) self._check_is_required(ci and ci.type_id or type_id, attr, v, type_attr=type_attr)
if attr.is_reference:
return v
if v == "" and attr.value_type not in (ValueTypeEnum.TEXT,): if v == "" and attr.value_type not in (ValueTypeEnum.TEXT,):
v = None v = None

View File

@ -58,7 +58,7 @@ def _request_messenger(subject, body, tos, sender, payload):
def notify_send(subject, body, methods, tos, payload=None): def notify_send(subject, body, methods, tos, payload=None):
payload = payload or {} payload = payload or {}
payload = {k: v or '' for k, v in payload.items()} payload = {k: '' if v is None else v for k, v in payload.items()}
subject = Template(subject).render(payload) subject = Template(subject).render(payload)
body = Template(body).render(payload) body = Template(body).render(payload)

View File

@ -105,6 +105,10 @@ class Attribute(Model):
is_password = db.Column(db.Boolean, default=False) is_password = db.Column(db.Boolean, default=False)
is_sortable = db.Column(db.Boolean, default=False) is_sortable = db.Column(db.Boolean, default=False)
is_dynamic = db.Column(db.Boolean, default=False) is_dynamic = db.Column(db.Boolean, default=False)
is_bool = db.Column(db.Boolean, default=False)
is_reference = db.Column(db.Boolean, default=False)
reference_type_id = db.Column(db.Integer, db.ForeignKey('c_ci_types.id'))
default = db.Column(db.JSON) # {"default": None} default = db.Column(db.JSON) # {"default": None}

View File

@ -20,10 +20,12 @@ 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 REDIS_PREFIX_CI_RELATION2
from api.lib.cmdb.const import RelationSourceEnum from api.lib.cmdb.const import RelationSourceEnum
from api.lib.cmdb.perms import CIFilterPermsCRUD from api.lib.cmdb.perms import CIFilterPermsCRUD
from api.lib.cmdb.utils import TableMap
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
from api.lib.utils import handle_arg_list from api.lib.utils import handle_arg_list
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 AutoDiscoveryCITypeRelation
@ -84,7 +86,7 @@ def batch_ci_cache(ci_ids, ): # only for attribute change index
@celery.task(name="cmdb.ci_delete", queue=CMDB_QUEUE) @celery.task(name="cmdb.ci_delete", queue=CMDB_QUEUE)
@reconnect_db @reconnect_db
def ci_delete(ci_id): def ci_delete(ci_id, type_id):
current_app.logger.info(ci_id) current_app.logger.info(ci_id)
if current_app.config.get("USE_ES"): if current_app.config.get("USE_ES"):
@ -99,6 +101,12 @@ def ci_delete(ci_id):
adt.update(updated_at=datetime.datetime.now()) adt.update(updated_at=datetime.datetime.now())
instance.delete() instance.delete()
for attr in Attribute.get_by(reference_type_id=type_id, to_dict=False):
table = TableMap(attr=attr).table
for i in getattr(table, 'get_by')(attr_id=attr.id, value=ci_id, to_dict=False):
i.delete()
ci_cache(i.ci_id, None, None)
current_app.logger.info("{0} delete..........".format(ci_id)) current_app.logger.info("{0} delete..........".format(ci_id))

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2024-06-20 19:12+0800\n" "POT-Creation-Date: 2024-08-20 13:47+0800\n"
"PO-Revision-Date: 2023-12-25 20:21+0800\n" "PO-Revision-Date: 2023-12-25 20:21+0800\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: zh\n" "Language: zh\n"
@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.14.0\n" "Generated-By: Babel 2.16.0\n"
#: api/lib/resp_format.py:7 #: api/lib/resp_format.py:7
msgid "unauthorized" msgid "unauthorized"
@ -169,8 +169,8 @@ msgstr "目前只允许 属性创建人、管理员 删除属性!"
#: api/lib/cmdb/resp_format.py:37 #: api/lib/cmdb/resp_format.py:37
msgid "" msgid ""
"Attribute field names cannot be built-in fields: id, _id, ci_id, type, " "Attribute field names cannot be built-in fields: id, _id, ci_id, type, "
"_type, ci_type" "_type, ci_type, ticket_id"
msgstr "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type" msgstr "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type, ci_type, ticket_id"
#: api/lib/cmdb/resp_format.py:39 #: api/lib/cmdb/resp_format.py:39
msgid "Predefined value: Other model request parameters are illegal!" msgid "Predefined value: Other model request parameters are illegal!"
@ -197,286 +197,298 @@ msgid "CI already exists!"
msgstr "CI 已经存在!" msgstr "CI 已经存在!"
#: api/lib/cmdb/resp_format.py:47 #: api/lib/cmdb/resp_format.py:47
msgid "{}: CI reference {} does not exist!"
msgstr "{}: CI引用 {} 不存在!"
#: api/lib/cmdb/resp_format.py:48
msgid "{}: CI reference {} is illegal!"
msgstr "{}, CI引用 {} 不合法!"
#: api/lib/cmdb/resp_format.py:49
msgid "Relationship constraint: {}, verification failed" msgid "Relationship constraint: {}, verification failed"
msgstr "关系约束: {}, 校验失败" msgstr "关系约束: {}, 校验失败"
#: api/lib/cmdb/resp_format.py:49 #: api/lib/cmdb/resp_format.py:51
msgid "" msgid ""
"Many-to-many relationship constraint: Model {} <-> {} already has a many-" "Many-to-many relationship constraint: Model {} <-> {} already has a many-"
"to-many relationship!" "to-many relationship!"
msgstr "多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!" msgstr "多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!"
#: api/lib/cmdb/resp_format.py:52 #: api/lib/cmdb/resp_format.py:54
msgid "CI relationship: {} does not exist" msgid "CI relationship: {} does not exist"
msgstr "CI关系: {} 不存在" msgstr "CI关系: {} 不存在"
#: api/lib/cmdb/resp_format.py:55 #: api/lib/cmdb/resp_format.py:57
msgid "In search expressions, not supported before parentheses: or, not" msgid "In search expressions, not supported before parentheses: or, not"
msgstr "搜索表达式里小括号前不支持: 或、非" msgstr "搜索表达式里小括号前不支持: 或、非"
#: api/lib/cmdb/resp_format.py:57 #: api/lib/cmdb/resp_format.py:59
msgid "Model {} does not exist" msgid "Model {} does not exist"
msgstr "模型 {} 不存在" msgstr "模型 {} 不存在"
#: api/lib/cmdb/resp_format.py:58 #: api/lib/cmdb/resp_format.py:60
msgid "Model {} already exists" msgid "Model {} already exists"
msgstr "模型 {} 已经存在" msgstr "模型 {} 已经存在"
#: api/lib/cmdb/resp_format.py:59 #: api/lib/cmdb/resp_format.py:61
msgid "The primary key is undefined or has been deleted" msgid "The primary key is undefined or has been deleted"
msgstr "主键未定义或者已被删除" msgstr "主键未定义或者已被删除"
#: api/lib/cmdb/resp_format.py:60 #: api/lib/cmdb/resp_format.py:62
msgid "Only the creator can delete it!" msgid "Only the creator can delete it!"
msgstr "只有创建人才能删除它!" msgstr "只有创建人才能删除它!"
#: api/lib/cmdb/resp_format.py:61 #: api/lib/cmdb/resp_format.py:63
msgid "The model cannot be deleted because the CI already exists" msgid "The model cannot be deleted because the CI already exists"
msgstr "因为CI已经存在不能删除模型" msgstr "因为CI已经存在不能删除模型"
#: api/lib/cmdb/resp_format.py:63 #: api/lib/cmdb/resp_format.py:65
msgid "The inheritance cannot be deleted because the CI already exists" msgid "The inheritance cannot be deleted because the CI already exists"
msgstr "因为CI已经存在不能删除继承关系" msgstr "因为CI已经存在不能删除继承关系"
#: api/lib/cmdb/resp_format.py:65 #: api/lib/cmdb/resp_format.py:67
msgid "The model is inherited and cannot be deleted" msgid "The model is inherited and cannot be deleted"
msgstr "该模型被继承, 不能删除" msgstr "该模型被继承, 不能删除"
#: api/lib/cmdb/resp_format.py:68 #: api/lib/cmdb/resp_format.py:68
msgid "The model is referenced by attribute {} and cannot be deleted"
msgstr "该模型被属性 {} 引用, 不能删除"
#: api/lib/cmdb/resp_format.py:72
msgid "" msgid ""
"The model cannot be deleted because the model is referenced by the " "The model cannot be deleted because the model is referenced by the "
"relational view {}" "relational view {}"
msgstr "因为关系视图 {} 引用了该模型,不能删除模型" msgstr "因为关系视图 {} 引用了该模型,不能删除模型"
#: api/lib/cmdb/resp_format.py:70 #: api/lib/cmdb/resp_format.py:74
msgid "Model group {} does not exist" msgid "Model group {} does not exist"
msgstr "模型分组 {} 不存在" msgstr "模型分组 {} 不存在"
#: api/lib/cmdb/resp_format.py:71 #: api/lib/cmdb/resp_format.py:75
msgid "Model group {} already exists" msgid "Model group {} already exists"
msgstr "模型分组 {} 已经存在" msgstr "模型分组 {} 已经存在"
#: api/lib/cmdb/resp_format.py:72 #: api/lib/cmdb/resp_format.py:76
msgid "Model relationship {} does not exist" msgid "Model relationship {} does not exist"
msgstr "模型关系 {} 不存在" msgstr "模型关系 {} 不存在"
#: api/lib/cmdb/resp_format.py:73 #: api/lib/cmdb/resp_format.py:77
msgid "Attribute group {} already exists" msgid "Attribute group {} already exists"
msgstr "属性分组 {} 已存在" msgstr "属性分组 {} 已存在"
#: api/lib/cmdb/resp_format.py:74 #: api/lib/cmdb/resp_format.py:78
msgid "Attribute group {} does not exist" msgid "Attribute group {} does not exist"
msgstr "属性分组 {} 不存在" msgstr "属性分组 {} 不存在"
#: api/lib/cmdb/resp_format.py:76 #: api/lib/cmdb/resp_format.py:80
msgid "Attribute group <{0}> - attribute <{1}> does not exist" msgid "Attribute group <{0}> - attribute <{1}> does not exist"
msgstr "属性组<{0}> - 属性<{1}> 不存在" msgstr "属性组<{0}> - 属性<{1}> 不存在"
#: api/lib/cmdb/resp_format.py:77 #: api/lib/cmdb/resp_format.py:81
msgid "The unique constraint already exists!" msgid "The unique constraint already exists!"
msgstr "唯一约束已经存在!" msgstr "唯一约束已经存在!"
#: api/lib/cmdb/resp_format.py:79 #: api/lib/cmdb/resp_format.py:83
msgid "Uniquely constrained attributes cannot be JSON and multi-valued" msgid "Uniquely constrained attributes cannot be JSON and multi-valued"
msgstr "唯一约束的属性不能是 JSON 和 多值" msgstr "唯一约束的属性不能是 JSON 和 多值"
#: api/lib/cmdb/resp_format.py:80 #: api/lib/cmdb/resp_format.py:84
msgid "Duplicated trigger" msgid "Duplicated trigger"
msgstr "重复的触发器" msgstr "重复的触发器"
#: api/lib/cmdb/resp_format.py:81 #: api/lib/cmdb/resp_format.py:85
msgid "Trigger {} does not exist" msgid "Trigger {} does not exist"
msgstr "触发器 {} 不存在" msgstr "触发器 {} 不存在"
#: api/lib/cmdb/resp_format.py:82 #: api/lib/cmdb/resp_format.py:86
msgid "Duplicated reconciliation rule" msgid "Duplicated reconciliation rule"
msgstr "" msgstr ""
#: api/lib/cmdb/resp_format.py:83 #: api/lib/cmdb/resp_format.py:87
msgid "Reconciliation rule {} does not exist" msgid "Reconciliation rule {} does not exist"
msgstr "关系类型 {} 不存在" msgstr "关系类型 {} 不存在"
#: api/lib/cmdb/resp_format.py:85 #: api/lib/cmdb/resp_format.py:89
msgid "Operation record {} does not exist" msgid "Operation record {} does not exist"
msgstr "操作记录 {} 不存在" msgstr "操作记录 {} 不存在"
#: api/lib/cmdb/resp_format.py:86 #: api/lib/cmdb/resp_format.py:90
msgid "Unique identifier cannot be deleted" msgid "Unique identifier cannot be deleted"
msgstr "不能删除唯一标识" msgstr "不能删除唯一标识"
#: api/lib/cmdb/resp_format.py:87 #: api/lib/cmdb/resp_format.py:91
msgid "Cannot delete default sorted attributes" msgid "Cannot delete default sorted attributes"
msgstr "不能删除默认排序的属性" msgstr "不能删除默认排序的属性"
#: api/lib/cmdb/resp_format.py:89 #: api/lib/cmdb/resp_format.py:93
msgid "No node selected" msgid "No node selected"
msgstr "没有选择节点" msgstr "没有选择节点"
#: api/lib/cmdb/resp_format.py:90 #: api/lib/cmdb/resp_format.py:94
msgid "This search option does not exist!" msgid "This search option does not exist!"
msgstr "该搜索选项不存在!" msgstr "该搜索选项不存在!"
#: api/lib/cmdb/resp_format.py:91 #: api/lib/cmdb/resp_format.py:95
msgid "This search option has a duplicate name!" msgid "This search option has a duplicate name!"
msgstr "该搜索选项命名重复!" msgstr "该搜索选项命名重复!"
#: api/lib/cmdb/resp_format.py:93 #: api/lib/cmdb/resp_format.py:97
msgid "Relationship type {} already exists" msgid "Relationship type {} already exists"
msgstr "关系类型 {} 已经存在" msgstr "关系类型 {} 已经存在"
#: api/lib/cmdb/resp_format.py:94 #: api/lib/cmdb/resp_format.py:98
msgid "Relationship type {} does not exist" msgid "Relationship type {} does not exist"
msgstr "关系类型 {} 不存在" msgstr "关系类型 {} 不存在"
#: api/lib/cmdb/resp_format.py:96 #: api/lib/cmdb/resp_format.py:100
msgid "Invalid attribute value: {}" msgid "Invalid attribute value: {}"
msgstr "无效的属性值: {}" msgstr "无效的属性值: {}"
#: api/lib/cmdb/resp_format.py:97 #: api/lib/cmdb/resp_format.py:101
msgid "{} Invalid value: {}" msgid "{} Invalid value: {}"
msgstr "{} 无效的值: {}" msgstr "{} 无效的值: {}"
#: api/lib/cmdb/resp_format.py:98 #: api/lib/cmdb/resp_format.py:102
msgid "{} is not in the predefined values" msgid "{} is not in the predefined values"
msgstr "{} 不在预定义值里" msgstr "{} 不在预定义值里"
#: api/lib/cmdb/resp_format.py:100 #: api/lib/cmdb/resp_format.py:104
msgid "The value of attribute {} must be unique, {} already exists" msgid "The value of attribute {} must be unique, {} already exists"
msgstr "属性 {} 的值必须是唯一的, 当前值 {} 已存在" msgstr "属性 {} 的值必须是唯一的, 当前值 {} 已存在"
#: api/lib/cmdb/resp_format.py:101 #: api/lib/cmdb/resp_format.py:105
msgid "Attribute {} value must exist" msgid "Attribute {} value must exist"
msgstr "属性 {} 值必须存在" msgstr "属性 {} 值必须存在"
#: api/lib/cmdb/resp_format.py:102 #: api/lib/cmdb/resp_format.py:106
msgid "Out of range value, the maximum value is 2147483647" msgid "Out of range value, the maximum value is 2147483647"
msgstr "超过最大值限制, 最大值是2147483647" msgstr "超过最大值限制, 最大值是2147483647"
#: api/lib/cmdb/resp_format.py:104 #: api/lib/cmdb/resp_format.py:108
msgid "Unknown error when adding or modifying attribute value: {}" msgid "Unknown error when adding or modifying attribute value: {}"
msgstr "新增或者修改属性值未知错误: {}" msgstr "新增或者修改属性值未知错误: {}"
#: api/lib/cmdb/resp_format.py:106 #: api/lib/cmdb/resp_format.py:110
msgid "Duplicate custom name" msgid "Duplicate custom name"
msgstr "订制名重复" msgstr "订制名重复"
#: api/lib/cmdb/resp_format.py:108 #: api/lib/cmdb/resp_format.py:112
msgid "Number of models exceeds limit: {}" msgid "Number of models exceeds limit: {}"
msgstr "模型数超过限制: {}" msgstr "模型数超过限制: {}"
#: api/lib/cmdb/resp_format.py:109 #: api/lib/cmdb/resp_format.py:113
msgid "The number of CIs exceeds the limit: {}" msgid "The number of CIs exceeds the limit: {}"
msgstr "CI数超过限制: {}" msgstr "CI数超过限制: {}"
#: api/lib/cmdb/resp_format.py:111 #: api/lib/cmdb/resp_format.py:115
msgid "Auto-discovery rule: {} already exists!" msgid "Auto-discovery rule: {} already exists!"
msgstr "自动发现规则: {} 已经存在!" msgstr "自动发现规则: {} 已经存在!"
#: api/lib/cmdb/resp_format.py:112 #: api/lib/cmdb/resp_format.py:116
msgid "Auto-discovery rule: {} does not exist!" msgid "Auto-discovery rule: {} does not exist!"
msgstr "自动发现规则: {} 不存在!" msgstr "自动发现规则: {} 不存在!"
#: api/lib/cmdb/resp_format.py:114 #: api/lib/cmdb/resp_format.py:118
msgid "This auto-discovery rule is referenced by the model and cannot be deleted!" msgid "This auto-discovery rule is referenced by the model and cannot be deleted!"
msgstr "该自动发现规则被模型引用, 不能删除!" msgstr "该自动发现规则被模型引用, 不能删除!"
#: api/lib/cmdb/resp_format.py:116 #: api/lib/cmdb/resp_format.py:120
msgid "The application of auto-discovery rules cannot be defined repeatedly!" msgid "The application of auto-discovery rules cannot be defined repeatedly!"
msgstr "自动发现规则的应用不能重复定义!" msgstr "自动发现规则的应用不能重复定义!"
#: api/lib/cmdb/resp_format.py:117 #: api/lib/cmdb/resp_format.py:121
msgid "The auto-discovery you want to modify: {} does not exist!" msgid "The auto-discovery you want to modify: {} does not exist!"
msgstr "您要修改的自动发现: {} 不存在!" msgstr "您要修改的自动发现: {} 不存在!"
#: api/lib/cmdb/resp_format.py:118 #: api/lib/cmdb/resp_format.py:122
msgid "Attribute does not include unique identifier: {}" msgid "Attribute does not include unique identifier: {}"
msgstr "属性字段没有包括唯一标识: {}" msgstr "属性字段没有包括唯一标识: {}"
#: api/lib/cmdb/resp_format.py:119 #: api/lib/cmdb/resp_format.py:123
msgid "The auto-discovery instance does not exist!" msgid "The auto-discovery instance does not exist!"
msgstr "自动发现的实例不存在!" msgstr "自动发现的实例不存在!"
#: api/lib/cmdb/resp_format.py:120 #: api/lib/cmdb/resp_format.py:124
msgid "The model is not associated with this auto-discovery!" msgid "The model is not associated with this auto-discovery!"
msgstr "模型并未关联该自动发现!" msgstr "模型并未关联该自动发现!"
#: api/lib/cmdb/resp_format.py:121 #: api/lib/cmdb/resp_format.py:125
msgid "Only the creator can modify the Secret!" msgid "Only the creator can modify the Secret!"
msgstr "只有创建人才能修改Secret!" msgstr "只有创建人才能修改Secret!"
#: api/lib/cmdb/resp_format.py:123 #: api/lib/cmdb/resp_format.py:127
msgid "This rule already has auto-discovery instances and cannot be deleted!" msgid "This rule already has auto-discovery instances and cannot be deleted!"
msgstr "该规则已经有自动发现的实例, 不能被删除!" msgstr "该规则已经有自动发现的实例, 不能被删除!"
#: api/lib/cmdb/resp_format.py:125 #: api/lib/cmdb/resp_format.py:129
msgid "The default auto-discovery rule is already referenced by model {}!" msgid "The default auto-discovery rule is already referenced by model {}!"
msgstr "该默认的自动发现规则 已经被模型 {} 引用!" msgstr "该默认的自动发现规则 已经被模型 {} 引用!"
#: api/lib/cmdb/resp_format.py:127 #: api/lib/cmdb/resp_format.py:131
msgid "The unique_key method must return a non-empty string!" msgid "The unique_key method must return a non-empty string!"
msgstr "unique_key方法必须返回非空字符串!" msgstr "unique_key方法必须返回非空字符串!"
#: api/lib/cmdb/resp_format.py:128 #: api/lib/cmdb/resp_format.py:132
msgid "The attributes method must return a list" msgid "The attributes method must return a list"
msgstr "attributes方法必须返回的是list" msgstr "attributes方法必须返回的是list"
#: api/lib/cmdb/resp_format.py:130 #: api/lib/cmdb/resp_format.py:134
msgid "The list returned by the attributes method cannot be empty!" msgid "The list returned by the attributes method cannot be empty!"
msgstr "attributes方法返回的list不能为空!" msgstr "attributes方法返回的list不能为空!"
#: api/lib/cmdb/resp_format.py:132 #: api/lib/cmdb/resp_format.py:136
msgid "Only administrators can define execution targets as: all nodes!" msgid "Only administrators can define execution targets as: all nodes!"
msgstr "只有管理员才可以定义执行机器为: 所有节点!" msgstr "只有管理员才可以定义执行机器为: 所有节点!"
#: api/lib/cmdb/resp_format.py:133 #: api/lib/cmdb/resp_format.py:137
msgid "Execute targets permission check failed: {}" msgid "Execute targets permission check failed: {}"
msgstr "执行机器权限检查不通过: {}" msgstr "执行机器权限检查不通过: {}"
#: api/lib/cmdb/resp_format.py:135 #: api/lib/cmdb/resp_format.py:139
msgid "CI filter authorization must be named!" msgid "CI filter authorization must be named!"
msgstr "CI过滤授权 必须命名!" msgstr "CI过滤授权 必须命名!"
#: api/lib/cmdb/resp_format.py:136 #: api/lib/cmdb/resp_format.py:140
msgid "CI filter authorization is currently not supported or query" msgid "CI filter authorization is currently not supported or query"
msgstr "CI过滤授权 暂时不支持 或 查询" msgstr "CI过滤授权 暂时不支持 或 查询"
#: api/lib/cmdb/resp_format.py:139 #: api/lib/cmdb/resp_format.py:143
msgid "You do not have permission to operate attribute {}!" msgid "You do not have permission to operate attribute {}!"
msgstr "您没有属性 {} 的操作权限!" msgstr "您没有属性 {} 的操作权限!"
#: api/lib/cmdb/resp_format.py:140 #: api/lib/cmdb/resp_format.py:144
msgid "You do not have permission to operate this CI!" msgid "You do not have permission to operate this CI!"
msgstr "您没有该CI的操作权限!" msgstr "您没有该CI的操作权限!"
#: api/lib/cmdb/resp_format.py:142 #: api/lib/cmdb/resp_format.py:146
msgid "Failed to save password: {}" msgid "Failed to save password: {}"
msgstr "保存密码失败: {}" msgstr "保存密码失败: {}"
#: api/lib/cmdb/resp_format.py:143 #: api/lib/cmdb/resp_format.py:147
msgid "Failed to get password: {}" msgid "Failed to get password: {}"
msgstr "获取密码失败: {}" msgstr "获取密码失败: {}"
#: api/lib/cmdb/resp_format.py:145 #: api/lib/cmdb/resp_format.py:149
msgid "Scheduling time format error" msgid "Scheduling time format error"
msgstr "{}格式错误,应该为:%Y-%m-%d %H:%M:%S" msgstr "{}格式错误,应该为:%Y-%m-%d %H:%M:%S"
#: api/lib/cmdb/resp_format.py:146 #: api/lib/cmdb/resp_format.py:150
msgid "CMDB data reconciliation results" msgid "CMDB data reconciliation results"
msgstr "" msgstr ""
#: api/lib/cmdb/resp_format.py:147 #: api/lib/cmdb/resp_format.py:151
msgid "Number of {} illegal: {}" msgid "Number of {} illegal: {}"
msgstr "" msgstr ""
#: api/lib/cmdb/resp_format.py:149 #: api/lib/cmdb/resp_format.py:153
msgid "Topology view {} already exists" msgid "Topology view {} already exists"
msgstr "拓扑视图 {} 已经存在" msgstr "拓扑视图 {} 已经存在"
#: api/lib/cmdb/resp_format.py:150 #: api/lib/cmdb/resp_format.py:154
msgid "Topology group {} already exists" msgid "Topology group {} already exists"
msgstr "拓扑视图分组 {} 已经存在" msgstr "拓扑视图分组 {} 已经存在"
#: api/lib/cmdb/resp_format.py:152 #: api/lib/cmdb/resp_format.py:156
msgid "The group cannot be deleted because the topology view already exists" msgid "The group cannot be deleted because the topology view already exists"
msgstr "因为该分组下定义了拓扑视图,不能删除" msgstr "因为该分组下定义了拓扑视图,不能删除"

View File

@ -15,7 +15,7 @@ from api.lib.cmdb.const import ResourceTypeEnum, PermEnum
from api.lib.cmdb.const import RetKey from api.lib.cmdb.const import RetKey
from api.lib.cmdb.perms import has_perm_for_ci from api.lib.cmdb.perms import has_perm_for_ci
from api.lib.cmdb.search import SearchError from api.lib.cmdb.search import SearchError
from api.lib.cmdb.search.ci import search from api.lib.cmdb.search.ci import search as ci_search
from api.lib.decorator import args_required from api.lib.decorator import args_required
from api.lib.perm.acl.acl import has_perm_from_args from api.lib.perm.acl.acl import has_perm_from_args
from api.lib.utils import get_page from api.lib.utils import get_page
@ -160,7 +160,7 @@ class CISearchView(APIView):
use_id_filter = request.values.get("use_id_filter", False) in current_app.config.get('BOOL_TRUE') use_id_filter = request.values.get("use_id_filter", False) in current_app.config.get('BOOL_TRUE')
start = time.time() start = time.time()
s = search(query, fl, facet, page, ret_key, count, sort, excludes, use_id_filter=use_id_filter) s = ci_search(query, fl, facet, page, ret_key, count, sort, excludes, use_id_filter=use_id_filter)
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:

View File

@ -48,16 +48,21 @@ class CITypeView(APIView):
if request.url.endswith("icons"): if request.url.endswith("icons"):
return self.jsonify(CITypeManager().get_icons()) return self.jsonify(CITypeManager().get_icons())
q = request.args.get("type_name") q = request.values.get("type_name")
type_ids = handle_arg_list(request.values.get("type_ids"))
if type_id is not None: type_ids = type_ids or (type_id and [type_id])
ci_type = CITypeCache.get(type_id) if type_ids:
ci_types = []
for _type_id in type_ids:
ci_type = CITypeCache.get(_type_id)
if ci_type is None: if ci_type is None:
return abort(404, ErrFormat.ci_type_not_found) return abort(404, ErrFormat.ci_type_not_found)
ci_type = ci_type.to_dict() ci_type = ci_type.to_dict()
ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(type_id) ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(_type_id)
ci_types = [ci_type] ci_type['show_name'] = ci_type.get('show_id') and AttributeCache.get(ci_type['show_id']).name
ci_type['unique_name'] = ci_type['unique_id'] and AttributeCache.get(ci_type['unique_id']).name
ci_types.append(ci_type)
elif type_name is not None: elif type_name is not None:
ci_type = CITypeCache.get(type_name).to_dict() ci_type = CITypeCache.get(type_name).to_dict()
ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(ci_type['id']) ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(ci_type['id'])