diff --git a/cmdb-api/api/lib/cmdb/attribute.py b/cmdb-api/api/lib/cmdb/attribute.py index f2e3d69..a7579dc 100644 --- a/cmdb-api/api/lib/cmdb/attribute.py +++ b/cmdb-api/api/lib/cmdb/attribute.py @@ -167,24 +167,30 @@ class AttributeManager(object): def get_attribute_by_name(self, name): attr = Attribute.get_by(name=name, first=True) if attr.get("is_choice"): - attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], - attr["choice_web_hook"], attr.get("choice_other")) + attr["choice_value"] = self.get_choice_values(attr["id"], + attr["value_type"], + attr["choice_web_hook"], + attr.get("choice_other")) return attr def get_attribute_by_alias(self, alias): attr = Attribute.get_by(alias=alias, first=True) if attr.get("is_choice"): - attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], - attr["choice_web_hook"], attr.get("choice_other")) + attr["choice_value"] = self.get_choice_values(attr["id"], + attr["value_type"], + attr["choice_web_hook"], + attr.get("choice_other")) return attr def get_attribute_by_id(self, _id): attr = Attribute.get_by_id(_id).to_dict() if attr.get("is_choice"): - attr["choice_value"] = self.get_choice_values(attr["id"], attr["value_type"], - attr["choice_web_hook"], attr.get("choice_other")) + attr["choice_value"] = self.get_choice_values(attr["id"], + attr["value_type"], + attr["choice_web_hook"], + attr.get("choice_other")) return attr diff --git a/cmdb-api/api/lib/cmdb/ci.py b/cmdb-api/api/lib/cmdb/ci.py index e2cc6c5..a6d5c76 100644 --- a/cmdb-api/api/lib/cmdb/ci.py +++ b/cmdb-api/api/lib/cmdb/ci.py @@ -266,7 +266,7 @@ class CIManager(object): value_table = TableMap(attr_name=id2name[attr_id]).table 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( CI, CI.id == value_table.ci_id).filter(CI.type_id == type_id) _ci_ids = set([i.ci_id for i in values]) @@ -292,6 +292,53 @@ class CIManager(object): 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 def add(cls, ci_type_name, exist_policy=ExistPolicy.REPLACE, @@ -392,6 +439,8 @@ class CIManager(object): if attr.re_check and password_dict.get(attr.id): 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) @@ -443,9 +492,10 @@ class CIManager(object): 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') ci = self.confirm_ci_existed(ci_id) + ci_type = ci.ci_type attrs = CITypeAttributeManager.get_all_attributes(ci.type_id) 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): 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 {} 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() 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) 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) else: 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} if ref_ci_dict: - if not __sync: + if not _sync: ci_relation_add.apply_async(args=(ref_ci_dict, ci.id), queue=CMDB_QUEUE) else: ci_relation_add(ref_ci_dict, ci.id) @@ -578,7 +630,7 @@ class CIManager(object): if ci_dict: 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) return ci_id @@ -773,7 +825,7 @@ class CIManager(object): value_table = ValueTypeMap.table[ValueTypeEnum.PASSWORD] if current_app.config.get('SECRETS_ENGINE') == 'inner': if value: - encrypt_value, status = InnerCrypt().encrypt(value) + encrypt_value, status = InnerCrypt().encrypt(str(value)) if not status: current_app.logger.error('save password 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')) if value: 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: current_app.logger.error('save password to vault failed: {}'.format(e)) return abort(400, ErrFormat.password_save_failed.format('write vault failed')) diff --git a/cmdb-api/api/lib/cmdb/ci_type.py b/cmdb-api/api/lib/cmdb/ci_type.py index 8bffd58..8b98316 100644 --- a/cmdb-api/api/lib/cmdb/ci_type.py +++ b/cmdb-api/api/lib/cmdb/ci_type.py @@ -145,7 +145,7 @@ class CITypeManager(object): kwargs["alias"] = kwargs["name"] if not kwargs.get("alias") else kwargs["alias"] 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['uid'] = current_user.uid @@ -184,7 +184,7 @@ class CITypeManager(object): 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, 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 = AttributeCache.get(unique_key) @@ -234,6 +234,10 @@ class CITypeManager(object): if CITypeInheritance.get_by(parent_id=type_id, first=True): 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) for rv in relation_views: for item in (rv.cr_ids or []): @@ -1323,6 +1327,7 @@ class CITypeTemplateManager(object): def _import_attributes(self, type2attributes): attributes = [attr for type_id in type2attributes for attr in type2attributes[type_id]] attrs = [] + references = [] for i in copy.deepcopy(attributes): if i.pop('inherited', None): continue @@ -1337,6 +1342,10 @@ class CITypeTemplateManager(object): if not choice_value: 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)) 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'): 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): for i in ci_types: @@ -1359,6 +1368,11 @@ class CITypeTemplateManager(object): 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): _ci_type_groups = copy.deepcopy(ci_type_groups) for i in _ci_type_groups: @@ -1584,13 +1598,15 @@ class CITypeTemplateManager(object): import 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)) s = time.time() 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)) + self._import_reference_attributes(references, ci_type_id_map) + s = time.time() 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)) @@ -1675,6 +1691,16 @@ class CITypeTemplateManager(object): type_ids.extend(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( ci_types=ci_types, relation_types=[i.to_dict() for i in RelationTypeManager.get_all()], @@ -1687,6 +1713,7 @@ class CITypeTemplateManager(object): icons=dict() ) 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): try: diff --git a/cmdb-api/api/lib/cmdb/const.py b/cmdb-api/api/lib/cmdb/const.py index 972fa69..1cfd43f 100644 --- a/cmdb-api/api/lib/cmdb/const.py +++ b/cmdb-api/api/lib/cmdb/const.py @@ -14,6 +14,8 @@ class ValueTypeEnum(BaseEnum): JSON = "6" PASSWORD = TEXT LINK = TEXT + BOOL = "7" + REFERENCE = INT class ConstraintEnum(BaseEnum): diff --git a/cmdb-api/api/lib/cmdb/resp_format.py b/cmdb-api/api/lib/cmdb/resp_format.py index 57a5b65..0a39341 100644 --- a/cmdb-api/api/lib/cmdb/resp_format.py +++ b/cmdb-api/api/lib/cmdb/resp_format.py @@ -44,6 +44,8 @@ class ErrFormat(CommonErrFormat): unique_value_not_found = _l("The model's primary key {} does not exist!") # 模型的主键 {} 不存在! unique_key_required = _l("Primary key {} is missing") # 主键字段 {} 缺失 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") # 关系约束: {}, 校验失败 # 多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系! m2m_relation_constraint = _l( @@ -63,6 +65,8 @@ class ErrFormat(CommonErrFormat): ci_exists_and_cannot_delete_inheritance = _l( "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_referenced_cannot_delete = _l( + "The model is referenced by attribute {} and cannot be deleted") # 该模型被属性 {} 引用, 不能删除 # 因为关系视图 {} 引用了该模型,不能删除模型 ci_relation_view_exists_and_cannot_delete_type = _l( diff --git a/cmdb-api/api/lib/cmdb/search/ci/db/search.py b/cmdb-api/api/lib/cmdb/search/ci/db/search.py index 766413c..2aaa690 100644 --- a/cmdb-api/api/lib/cmdb/search/ci/db/search.py +++ b/cmdb-api/api/lib/cmdb/search/ci/db/search.py @@ -451,6 +451,9 @@ class Search(object): if field_type == ValueTypeEnum.DATE and len(v) == 10: 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 if v.startswith("(") and v.endswith(")"): _query_sql = self._in_query_handler(attr, v, is_not) diff --git a/cmdb-api/api/lib/cmdb/utils.py b/cmdb-api/api/lib/cmdb/utils.py index ab354ea..7ea7e2a 100644 --- a/cmdb-api/api/lib/cmdb/utils.py +++ b/cmdb-api/api/lib/cmdb/utils.py @@ -7,6 +7,7 @@ import json import re import six +from flask import current_app import api.models.cmdb as model from api.lib.cmdb.cache import AttributeCache @@ -64,6 +65,7 @@ class ValueTypeMap(object): ValueTypeEnum.DATETIME: str2datetime, ValueTypeEnum.DATE: str2date, 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 = { @@ -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.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.BOOL: lambda x: x in current_app.config.get('BOOL_TRUE'), } 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.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.BOOL: lambda x: x in current_app.config.get('BOOL_TRUE'), } choice = { @@ -105,6 +109,7 @@ class ValueTypeMap(object): 'index_{0}'.format(ValueTypeEnum.TIME): model.CIIndexValueText, 'index_{0}'.format(ValueTypeEnum.FLOAT): model.CIIndexValueFloat, 'index_{0}'.format(ValueTypeEnum.JSON): model.CIValueJson, + 'index_{0}'.format(ValueTypeEnum.BOOL): model.CIIndexValueInteger, } table_name = { @@ -117,6 +122,7 @@ class ValueTypeMap(object): 'index_{0}'.format(ValueTypeEnum.TIME): 'c_value_index_texts', 'index_{0}'.format(ValueTypeEnum.FLOAT): 'c_value_index_floats', 'index_{0}'.format(ValueTypeEnum.JSON): 'c_value_json', + 'index_{0}'.format(ValueTypeEnum.BOOL): 'c_value_index_integers', } es_type = { diff --git a/cmdb-api/api/lib/cmdb/value.py b/cmdb-api/api/lib/cmdb/value.py index 8fe4fcf..0edc2b3 100644 --- a/cmdb-api/api/lib/cmdb/value.py +++ b/cmdb-api/api/lib/cmdb/value.py @@ -128,14 +128,20 @@ class AttributeValueManager(object): 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): - ci = ci or {} - v = self._deserialize_value(attr.alias, attr.value_type, value) + if not attr.is_reference: + ci = ci or {} + 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) + + else: + v = value or None - attr.is_choice and value and self._check_is_choice(attr, attr.value_type, v) 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) - 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,): v = None diff --git a/cmdb-api/api/lib/notify.py b/cmdb-api/api/lib/notify.py index 8d6e070..75921a0 100644 --- a/cmdb-api/api/lib/notify.py +++ b/cmdb-api/api/lib/notify.py @@ -58,7 +58,7 @@ def _request_messenger(subject, body, tos, sender, payload): def notify_send(subject, body, methods, tos, payload=None): 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) body = Template(body).render(payload) diff --git a/cmdb-api/api/models/cmdb.py b/cmdb-api/api/models/cmdb.py index 5e4f73c..d310afa 100644 --- a/cmdb-api/api/models/cmdb.py +++ b/cmdb-api/api/models/cmdb.py @@ -105,6 +105,10 @@ class Attribute(Model): is_password = db.Column(db.Boolean, default=False) is_sortable = 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} diff --git a/cmdb-api/api/tasks/cmdb.py b/cmdb-api/api/tasks/cmdb.py index c0bde06..fd00c87 100644 --- a/cmdb-api/api/tasks/cmdb.py +++ b/cmdb-api/api/tasks/cmdb.py @@ -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 RelationSourceEnum 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 reconnect_db from api.lib.perm.acl.cache import UserCache 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 AutoDiscoveryCIType 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) @reconnect_db -def ci_delete(ci_id): +def ci_delete(ci_id, type_id): current_app.logger.info(ci_id) if current_app.config.get("USE_ES"): @@ -99,6 +101,12 @@ def ci_delete(ci_id): adt.update(updated_at=datetime.datetime.now()) 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)) diff --git a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.mo b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.mo index a62ae1d..8b6e43b 100644 Binary files a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.mo and b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.mo differ diff --git a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po index 38c8949..dd3f1bc 100644 --- a/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po +++ b/cmdb-api/api/translations/zh/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\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" "Last-Translator: FULL NAME \n" "Language: zh\n" @@ -16,7 +16,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\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 msgid "unauthorized" @@ -169,8 +169,8 @@ msgstr "目前只允许 属性创建人、管理员 删除属性!" #: api/lib/cmdb/resp_format.py:37 msgid "" "Attribute field names cannot be built-in fields: id, _id, ci_id, type, " -"_type, ci_type" -msgstr "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type" +"_type, ci_type, ticket_id" +msgstr "属性字段名不能是内置字段: id, _id, ci_id, type, _type, ci_type, ci_type, ticket_id" #: api/lib/cmdb/resp_format.py:39 msgid "Predefined value: Other model request parameters are illegal!" @@ -197,286 +197,298 @@ msgid "CI already exists!" msgstr "CI 已经存在!" #: 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" msgstr "关系约束: {}, 校验失败" -#: api/lib/cmdb/resp_format.py:49 +#: api/lib/cmdb/resp_format.py:51 msgid "" "Many-to-many relationship constraint: Model {} <-> {} already has a many-" "to-many relationship!" msgstr "多对多关系 限制: 模型 {} <-> {} 已经存在多对多关系!" -#: api/lib/cmdb/resp_format.py:52 +#: api/lib/cmdb/resp_format.py:54 msgid "CI relationship: {} does not exist" 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" msgstr "搜索表达式里小括号前不支持: 或、非" -#: api/lib/cmdb/resp_format.py:57 +#: api/lib/cmdb/resp_format.py:59 msgid "Model {} does not exist" msgstr "模型 {} 不存在" -#: api/lib/cmdb/resp_format.py:58 +#: api/lib/cmdb/resp_format.py:60 msgid "Model {} already exists" 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" msgstr "主键未定义或者已被删除" -#: api/lib/cmdb/resp_format.py:60 +#: api/lib/cmdb/resp_format.py:62 msgid "Only the creator can delete it!" 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" 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" 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" msgstr "该模型被继承, 不能删除" #: 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 "" "The model cannot be deleted because the model is referenced by the " "relational view {}" msgstr "因为关系视图 {} 引用了该模型,不能删除模型" -#: api/lib/cmdb/resp_format.py:70 +#: api/lib/cmdb/resp_format.py:74 msgid "Model group {} does not exist" msgstr "模型分组 {} 不存在" -#: api/lib/cmdb/resp_format.py:71 +#: api/lib/cmdb/resp_format.py:75 msgid "Model group {} already exists" msgstr "模型分组 {} 已经存在" -#: api/lib/cmdb/resp_format.py:72 +#: api/lib/cmdb/resp_format.py:76 msgid "Model relationship {} does not exist" msgstr "模型关系 {} 不存在" -#: api/lib/cmdb/resp_format.py:73 +#: api/lib/cmdb/resp_format.py:77 msgid "Attribute group {} already exists" msgstr "属性分组 {} 已存在" -#: api/lib/cmdb/resp_format.py:74 +#: api/lib/cmdb/resp_format.py:78 msgid "Attribute group {} does not exist" msgstr "属性分组 {} 不存在" -#: api/lib/cmdb/resp_format.py:76 +#: api/lib/cmdb/resp_format.py:80 msgid "Attribute group <{0}> - attribute <{1}> does not exist" msgstr "属性组<{0}> - 属性<{1}> 不存在" -#: api/lib/cmdb/resp_format.py:77 +#: api/lib/cmdb/resp_format.py:81 msgid "The unique constraint already exists!" 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" msgstr "唯一约束的属性不能是 JSON 和 多值" -#: api/lib/cmdb/resp_format.py:80 +#: api/lib/cmdb/resp_format.py:84 msgid "Duplicated trigger" msgstr "重复的触发器" -#: api/lib/cmdb/resp_format.py:81 +#: api/lib/cmdb/resp_format.py:85 msgid "Trigger {} does not exist" msgstr "触发器 {} 不存在" -#: api/lib/cmdb/resp_format.py:82 +#: api/lib/cmdb/resp_format.py:86 msgid "Duplicated reconciliation rule" msgstr "" -#: api/lib/cmdb/resp_format.py:83 +#: api/lib/cmdb/resp_format.py:87 msgid "Reconciliation rule {} does not exist" msgstr "关系类型 {} 不存在" -#: api/lib/cmdb/resp_format.py:85 +#: api/lib/cmdb/resp_format.py:89 msgid "Operation record {} does not exist" msgstr "操作记录 {} 不存在" -#: api/lib/cmdb/resp_format.py:86 +#: api/lib/cmdb/resp_format.py:90 msgid "Unique identifier cannot be deleted" msgstr "不能删除唯一标识" -#: api/lib/cmdb/resp_format.py:87 +#: api/lib/cmdb/resp_format.py:91 msgid "Cannot delete default sorted attributes" msgstr "不能删除默认排序的属性" -#: api/lib/cmdb/resp_format.py:89 +#: api/lib/cmdb/resp_format.py:93 msgid "No node selected" msgstr "没有选择节点" -#: api/lib/cmdb/resp_format.py:90 +#: api/lib/cmdb/resp_format.py:94 msgid "This search option does not exist!" msgstr "该搜索选项不存在!" -#: api/lib/cmdb/resp_format.py:91 +#: api/lib/cmdb/resp_format.py:95 msgid "This search option has a duplicate name!" msgstr "该搜索选项命名重复!" -#: api/lib/cmdb/resp_format.py:93 +#: api/lib/cmdb/resp_format.py:97 msgid "Relationship type {} already exists" msgstr "关系类型 {} 已经存在" -#: api/lib/cmdb/resp_format.py:94 +#: api/lib/cmdb/resp_format.py:98 msgid "Relationship type {} does not exist" msgstr "关系类型 {} 不存在" -#: api/lib/cmdb/resp_format.py:96 +#: api/lib/cmdb/resp_format.py:100 msgid "Invalid attribute value: {}" msgstr "无效的属性值: {}" -#: api/lib/cmdb/resp_format.py:97 +#: api/lib/cmdb/resp_format.py:101 msgid "{} Invalid value: {}" msgstr "{} 无效的值: {}" -#: api/lib/cmdb/resp_format.py:98 +#: api/lib/cmdb/resp_format.py:102 msgid "{} is not in the predefined values" 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" msgstr "属性 {} 的值必须是唯一的, 当前值 {} 已存在" -#: api/lib/cmdb/resp_format.py:101 +#: api/lib/cmdb/resp_format.py:105 msgid "Attribute {} value must exist" 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" 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: {}" msgstr "新增或者修改属性值未知错误: {}" -#: api/lib/cmdb/resp_format.py:106 +#: api/lib/cmdb/resp_format.py:110 msgid "Duplicate custom name" msgstr "订制名重复" -#: api/lib/cmdb/resp_format.py:108 +#: api/lib/cmdb/resp_format.py:112 msgid "Number of models exceeds limit: {}" msgstr "模型数超过限制: {}" -#: api/lib/cmdb/resp_format.py:109 +#: api/lib/cmdb/resp_format.py:113 msgid "The number of CIs exceeds the limit: {}" msgstr "CI数超过限制: {}" -#: api/lib/cmdb/resp_format.py:111 +#: api/lib/cmdb/resp_format.py:115 msgid "Auto-discovery rule: {} already exists!" msgstr "自动发现规则: {} 已经存在!" -#: api/lib/cmdb/resp_format.py:112 +#: api/lib/cmdb/resp_format.py:116 msgid "Auto-discovery rule: {} does not exist!" 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!" 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!" 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!" msgstr "您要修改的自动发现: {} 不存在!" -#: api/lib/cmdb/resp_format.py:118 +#: api/lib/cmdb/resp_format.py:122 msgid "Attribute does not include unique identifier: {}" msgstr "属性字段没有包括唯一标识: {}" -#: api/lib/cmdb/resp_format.py:119 +#: api/lib/cmdb/resp_format.py:123 msgid "The auto-discovery instance does not exist!" 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!" msgstr "模型并未关联该自动发现!" -#: api/lib/cmdb/resp_format.py:121 +#: api/lib/cmdb/resp_format.py:125 msgid "Only the creator can modify the 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!" 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 {}!" 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!" 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" 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!" 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!" msgstr "只有管理员才可以定义执行机器为: 所有节点!" -#: api/lib/cmdb/resp_format.py:133 +#: api/lib/cmdb/resp_format.py:137 msgid "Execute targets permission check failed: {}" msgstr "执行机器权限检查不通过: {}" -#: api/lib/cmdb/resp_format.py:135 +#: api/lib/cmdb/resp_format.py:139 msgid "CI filter authorization must be named!" 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" 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 {}!" 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!" msgstr "您没有该CI的操作权限!" -#: api/lib/cmdb/resp_format.py:142 +#: api/lib/cmdb/resp_format.py:146 msgid "Failed to save password: {}" msgstr "保存密码失败: {}" -#: api/lib/cmdb/resp_format.py:143 +#: api/lib/cmdb/resp_format.py:147 msgid "Failed to get password: {}" msgstr "获取密码失败: {}" -#: api/lib/cmdb/resp_format.py:145 +#: api/lib/cmdb/resp_format.py:149 msgid "Scheduling time format error" 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" msgstr "" -#: api/lib/cmdb/resp_format.py:147 +#: api/lib/cmdb/resp_format.py:151 msgid "Number of {} illegal: {}" msgstr "" -#: api/lib/cmdb/resp_format.py:149 +#: api/lib/cmdb/resp_format.py:153 msgid "Topology view {} already exists" msgstr "拓扑视图 {} 已经存在" -#: api/lib/cmdb/resp_format.py:150 +#: api/lib/cmdb/resp_format.py:154 msgid "Topology group {} already exists" 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" msgstr "因为该分组下定义了拓扑视图,不能删除" diff --git a/cmdb-api/api/views/cmdb/ci.py b/cmdb-api/api/views/cmdb/ci.py index 9afd217..b53f0c6 100644 --- a/cmdb-api/api/views/cmdb/ci.py +++ b/cmdb-api/api/views/cmdb/ci.py @@ -15,7 +15,7 @@ from api.lib.cmdb.const import ResourceTypeEnum, PermEnum from api.lib.cmdb.const import RetKey from api.lib.cmdb.perms import has_perm_for_ci 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.perm.acl.acl import has_perm_from_args 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') 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: response, counter, total, page, numfound, facet = s.search() except SearchError as e: diff --git a/cmdb-api/api/views/cmdb/ci_type.py b/cmdb-api/api/views/cmdb/ci_type.py index bd17a97..04f02c9 100644 --- a/cmdb-api/api/views/cmdb/ci_type.py +++ b/cmdb-api/api/views/cmdb/ci_type.py @@ -48,16 +48,21 @@ class CITypeView(APIView): if request.url.endswith("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")) + type_ids = type_ids or (type_id and [type_id]) + if type_ids: + ci_types = [] + for _type_id in type_ids: + ci_type = CITypeCache.get(_type_id) + if ci_type is None: + return abort(404, ErrFormat.ci_type_not_found) - if type_id is not None: - ci_type = CITypeCache.get(type_id) - if ci_type is None: - return abort(404, ErrFormat.ci_type_not_found) - - ci_type = ci_type.to_dict() - ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(type_id) - ci_types = [ci_type] + ci_type = ci_type.to_dict() + ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(_type_id) + 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: ci_type = CITypeCache.get(type_name).to_dict() ci_type['parent_ids'] = CITypeInheritanceManager.get_parents(ci_type['id'])