Compare commits

..

3 Commits

Author SHA1 Message Date
pycook
07eb902583 feat(api): my preference support grouping 2024-05-18 22:54:07 +08:00
pycook
46b54bb7f2 fix(ui): some bugs (#512) 2024-05-17 12:07:56 +08:00
pycook
fe63310c4e Dev api 240517 (#511)
* fix(api): list values delete

* fix(acl): role rebuild cache
2024-05-17 11:20:53 +08:00
19 changed files with 211 additions and 69 deletions

View File

@@ -263,6 +263,7 @@ class CIManager(object):
ci_ids = None ci_ids = None
for attr_id in constraint.attr_ids: for attr_id in constraint.attr_ids:
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]) or None,
only_query=True).join( only_query=True).join(
@@ -438,7 +439,8 @@ class CIManager(object):
return ci.id return ci.id
def update(self, ci_id, _is_admin=False, ticket_id=None, **ci_dict): def update(self, ci_id, _is_admin=False, ticket_id=None, __sync=False, **ci_dict):
current_app.logger.info((ci_id, ci_dict, __sync))
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)
@@ -502,11 +504,17 @@ 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: # has change if record_id: # has change
ci_cache.apply_async(args=(ci_id, OperateType.UPDATE, record_id), queue=CMDB_QUEUE) 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} 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:
ci_relation_add.apply_async(args=(ref_ci_dict, ci.id), queue=CMDB_QUEUE) 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))
@staticmethod @staticmethod
def update_unique_value(ci_id, unique_name, unique_value): def update_unique_value(ci_id, unique_name, unique_value):
@@ -830,6 +838,12 @@ class CIManager(object):
return data.get('v') return data.get('v')
def baseline(self, ci_ids, before_date): def baseline(self, ci_ids, before_date):
"""
return CI changes
:param ci_ids:
:param before_date:
:return:
"""
ci_list = self.get_cis_by_ids(ci_ids, ret_key=RetKey.ALIAS) ci_list = self.get_cis_by_ids(ci_ids, ret_key=RetKey.ALIAS)
if not ci_list: if not ci_list:
return dict() return dict()
@@ -913,6 +927,44 @@ class CIManager(object):
return result return result
def baseline_cis(self, ci_ids, before_date, fl=None):
"""
return CI changes
:param ci_ids:
:param before_date:
:param fl:
:return:
"""
ci_list = self.get_cis_by_ids(ci_ids, fields=fl)
if not ci_list:
return []
id2ci = {ci['_id']: ci for ci in ci_list}
changed = AttributeHistoryManger.get_records_for_attributes(
before_date, None, None, 1, 100000, None, None, ci_ids=ci_ids, more=True)[1]
for records in changed:
for change in records[1]:
if change['is_computed'] or change['is_password']:
continue
if change.get('default') and change['default'].get('default') == AttributeDefaultValueEnum.UPDATED_AT:
continue
if change['is_list']:
old, new, value_type, operate_type, ci_id, attr_name = (
change['old'], change['new'], change['value_type'], change['operate_type'],
change['ci_id'], change['attr_name'])
old = ValueTypeMap.deserialize[value_type](old) if old else old
new = ValueTypeMap.deserialize[value_type](new) if new else new
if operate_type == OperateType.ADD and new in (id2ci[ci_id][attr_name] or []):
id2ci[ci_id][attr_name].remove(new)
elif operate_type == OperateType.DELETE and old not in id2ci[ci_id][attr_name]:
id2ci[ci_id][attr_name].append(old)
else:
id2ci[change['ci_id']][change['attr_name']] = change['old']
return list(id2ci.values())
def rollback(self, ci_id, before_date): def rollback(self, ci_id, before_date):
baseline_ci = self.baseline([ci_id], before_date) baseline_ci = self.baseline([ci_id], before_date)
@@ -1088,7 +1140,7 @@ class CIRelationManager(object):
relation_type_id or abort(404, ErrFormat.relation_not_found.format("{} -> {}".format( relation_type_id or abort(404, ErrFormat.relation_not_found.format("{} -> {}".format(
first_ci.ci_type.name, second_ci.ci_type.name))) first_ci.ci_type.name, second_ci.ci_type.name)))
if current_app.config.get('USE_ACL') and valid: if current_app.config.get('USE_ACL') and valid and current_user.username != 'worker':
resource_name = CITypeRelationManager.acl_resource_name(first_ci.ci_type.name, resource_name = CITypeRelationManager.acl_resource_name(first_ci.ci_type.name,
second_ci.ci_type.name) second_ci.ci_type.name)
if not ACLManager().has_permission( if not ACLManager().has_permission(
@@ -1122,7 +1174,7 @@ class CIRelationManager(object):
def delete(cr_id): def delete(cr_id):
cr = CIRelation.get_by_id(cr_id) or abort(404, ErrFormat.relation_not_found.format("id={}".format(cr_id))) cr = CIRelation.get_by_id(cr_id) or abort(404, ErrFormat.relation_not_found.format("id={}".format(cr_id)))
if current_app.config.get('USE_ACL'): if current_app.config.get('USE_ACL') and current_user.username != 'worker':
resource_name = CITypeRelationManager.acl_resource_name(cr.first_ci.ci_type.name, cr.second_ci.ci_type.name) resource_name = CITypeRelationManager.acl_resource_name(cr.first_ci.ci_type.name, cr.second_ci.ci_type.name)
if not ACLManager().has_permission( if not ACLManager().has_permission(
resource_name, resource_name,
@@ -1156,6 +1208,21 @@ class CIRelationManager(object):
return cr return cr
@classmethod
def delete_3(cls, first_ci_id, second_ci_id):
cr = CIRelation.get_by(first_ci_id=first_ci_id,
second_ci_id=second_ci_id,
to_dict=False,
first=True)
if cr is not None:
ci_relation_delete.apply_async(args=(first_ci_id, second_ci_id, cr.ancestor_ids), queue=CMDB_QUEUE)
delete_id_filter.apply_async(args=(second_ci_id,), queue=CMDB_QUEUE)
cls.delete(cr.id)
return cr
@classmethod @classmethod
def batch_update(cls, ci_ids, parents, children, ancestor_ids=None): def batch_update(cls, ci_ids, parents, children, ancestor_ids=None):
""" """

View File

@@ -5,7 +5,6 @@ import copy
import toposort import toposort
from flask import abort from flask import abort
from flask import current_app from flask import current_app
from flask import session
from flask_login import current_user from flask_login import current_user
from toposort import toposort_flatten from toposort import toposort_flatten
from werkzeug.exceptions import BadRequest from werkzeug.exceptions import BadRequest
@@ -28,9 +27,11 @@ from api.lib.cmdb.perms import CIFilterPermsCRUD
from api.lib.cmdb.relation_type import RelationTypeManager from api.lib.cmdb.relation_type import RelationTypeManager
from api.lib.cmdb.resp_format import ErrFormat from api.lib.cmdb.resp_format import ErrFormat
from api.lib.cmdb.value import AttributeValueManager from api.lib.cmdb.value import AttributeValueManager
from api.lib.common_setting.role_perm_base import CMDBApp
from api.lib.decorator import kwargs_required from api.lib.decorator import kwargs_required
from api.lib.perm.acl.acl import ACLManager from api.lib.perm.acl.acl import ACLManager
from api.lib.perm.acl.acl import is_app_admin from api.lib.perm.acl.acl import is_app_admin
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
@@ -130,7 +131,9 @@ class CITypeManager(object):
def add(cls, **kwargs): def add(cls, **kwargs):
if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'): if current_app.config.get('USE_ACL') and not is_app_admin('cmdb'):
if ErrFormat.ci_type_config not in {i['name'] for i in ACLManager().get_resources(ResourceTypeEnum.PAGE)}: if ErrFormat.ci_type_config not in {i['name'] for i in ACLManager().get_resources(ResourceTypeEnum.PAGE)}:
return abort(403, ErrFormat.no_permission2) app_cli = CMDBApp()
validate_permission(app_cli.op.Model_Configuration, app_cli.resource_type_name,
app_cli.op.create_CIType, app_cli.app_name)
unique_key = kwargs.pop("unique_key", None) or kwargs.pop("unique_id", None) unique_key = kwargs.pop("unique_key", None) or kwargs.pop("unique_id", None)
unique_key = AttributeCache.get(unique_key) or abort(404, ErrFormat.unique_key_not_define) unique_key = AttributeCache.get(unique_key) or abort(404, ErrFormat.unique_key_not_define)

View File

@@ -31,7 +31,8 @@ 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 CITypeGroup
from api.models.cmdb import CITypeGroupItem
class PreferenceManager(object): class PreferenceManager(object):
pref_attr_cls = PreferenceShowAttributes pref_attr_cls = PreferenceShowAttributes
@@ -43,22 +44,47 @@ class PreferenceManager(object):
def get_types(instance=False, tree=False): def get_types(instance=False, tree=False):
ci_type_order = sorted(PreferenceCITypeOrder.get_by(uid=current_user.uid, to_dict=False), key=lambda x: x.order) ci_type_order = sorted(PreferenceCITypeOrder.get_by(uid=current_user.uid, to_dict=False), key=lambda x: x.order)
type2group = {}
for i in db.session.query(CITypeGroupItem, CITypeGroup).join(
CITypeGroup, CITypeGroup.id == CITypeGroupItem.group_id).filter(
CITypeGroup.deleted.is_(False)).filter(CITypeGroupItem.deleted.is_(False)):
type2group[i.CITypeGroupItem.type_id] = i.CITypeGroup.to_dict()
types = db.session.query(PreferenceShowAttributes.type_id).filter( types = db.session.query(PreferenceShowAttributes.type_id).filter(
PreferenceShowAttributes.uid == current_user.uid).filter( PreferenceShowAttributes.uid == current_user.uid).filter(
PreferenceShowAttributes.deleted.is_(False)).group_by( PreferenceShowAttributes.deleted.is_(False)).group_by(
PreferenceShowAttributes.type_id).all() if instance else [] PreferenceShowAttributes.type_id).all() if instance else []
types = sorted(types, key=lambda x: {i.type_id: idx for idx, i in enumerate( types = sorted(types, key=lambda x: {i.type_id: idx for idx, i in enumerate(
ci_type_order) if not i.is_tree}.get(x.type_id, 1)) ci_type_order) if not i.is_tree}.get(x.type_id, 1))
group_types = []
other_types = []
group2idx = {}
type_ids = set()
for ci_type in types:
type_id = ci_type.type_id
type_ids.add(type_id)
type_dict = CITypeCache.get(type_id).to_dict()
if type_id not in type2group:
other_types.append(type_dict)
else:
group = type2group[type_id]
if group['id'] not in group2idx:
group_types.append(type2group[type_id])
group2idx[group['id']] = len(group_types) - 1
group_types[group2idx[group['id']]].setdefault('ci_types', []).append(type_dict)
if other_types:
group_types.append(dict(ci_types=other_types))
tree_types = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=False) if tree else [] tree_types = PreferenceTreeView.get_by(uid=current_user.uid, to_dict=False) if tree else []
tree_types = sorted(tree_types, key=lambda x: {i.type_id: idx for idx, i in enumerate( tree_types = sorted(tree_types, key=lambda x: {i.type_id: idx for idx, i in enumerate(
ci_type_order) if i.is_tree}.get(x.type_id, 1)) ci_type_order) if i.is_tree}.get(x.type_id, 1))
type_ids = [i.type_id for i in types + tree_types] tree_types = [CITypeCache.get(_type.type_id).to_dict() for _type in tree_types]
if types and tree_types: for _type in tree_types:
type_ids = set(type_ids) type_ids.add(_type['id'])
return dict(group_types=group_types, tree_types=tree_types, type_ids=list(type_ids))
return [CITypeCache.get(type_id).to_dict() for type_id in type_ids]
@staticmethod @staticmethod
def get_types2(instance=False, tree=False): def get_types2(instance=False, tree=False):

View File

@@ -140,3 +140,7 @@ class ErrFormat(CommonErrFormat):
password_save_failed = _l("Failed to save password: {}") # 保存密码失败: {} password_save_failed = _l("Failed to save password: {}") # 保存密码失败: {}
password_load_failed = _l("Failed to get password: {}") # 获取密码失败: {} password_load_failed = _l("Failed to get password: {}") # 获取密码失败: {}
cron_time_format_invalid = _l("Scheduling time format error") # 调度时间格式错误
reconciliation_title = _l("CMDB data reconciliation results") # CMDB数据合规检查结果
reconciliation_body = _l("Number of {} illegal: {}") # "{} 不合规数: {}"

View File

@@ -47,7 +47,8 @@ class Search(object):
excludes=None, excludes=None,
parent_node_perm_passed=False, parent_node_perm_passed=False,
use_id_filter=False, use_id_filter=False,
use_ci_filter=True): use_ci_filter=True,
only_ids=False):
self.orig_query = query self.orig_query = query
self.fl = fl or [] self.fl = fl or []
self.excludes = excludes or [] self.excludes = excludes or []
@@ -64,6 +65,7 @@ class Search(object):
self.parent_node_perm_passed = parent_node_perm_passed self.parent_node_perm_passed = parent_node_perm_passed
self.use_id_filter = use_id_filter self.use_id_filter = use_id_filter
self.use_ci_filter = use_ci_filter self.use_ci_filter = use_ci_filter
self.only_ids = only_ids
self.valid_type_names = [] self.valid_type_names = []
self.type2filter_perms = dict() self.type2filter_perms = dict()
@@ -590,6 +592,8 @@ class Search(object):
def search(self): def search(self):
numfound, ci_ids = self._query_build_raw() numfound, ci_ids = self._query_build_raw()
ci_ids = list(map(str, ci_ids)) ci_ids = list(map(str, ci_ids))
if self.only_ids:
return ci_ids
_fl = self._fl_build() _fl = self._fl_build()

View File

@@ -69,7 +69,7 @@ class Search(object):
if _l < int(level) and c == ConstraintEnum.Many2Many: if _l < int(level) and c == ConstraintEnum.Many2Many:
self.has_m2m = True self.has_m2m = True
self.type2filter_perms = None self.type2filter_perms = {}
self.is_app_admin = is_app_admin('cmdb') or current_user.username == "worker" self.is_app_admin = is_app_admin('cmdb') or current_user.username == "worker"
@@ -79,7 +79,7 @@ class Search(object):
key = [] key = []
_tmp = [] _tmp = []
for level in range(1, sorted(self.level)[-1] + 1): for level in range(1, sorted(self.level)[-1] + 1):
if len(self.descendant_ids) >= level and self.type2filter_perms.get(self.descendant_ids[level - 1]): if len(self.descendant_ids or []) >= level and self.type2filter_perms.get(self.descendant_ids[level - 1]):
id_filter_limit, _ = self._get_ci_filter(self.type2filter_perms[self.descendant_ids[level - 1]]) id_filter_limit, _ = self._get_ci_filter(self.type2filter_perms[self.descendant_ids[level - 1]])
else: else:
id_filter_limit = {} id_filter_limit = {}
@@ -151,9 +151,9 @@ class Search(object):
return True return True
def search(self): def search(self, only_ids=False):
use_ci_filter = len(self.descendant_ids) == self.level[0] - 1 use_ci_filter = len(self.descendant_ids or []) == self.level[0] - 1
parent_node_perm_passed = self._has_read_perm_from_parent_nodes() parent_node_perm_passed = not self.is_app_admin and self._has_read_perm_from_parent_nodes()
ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id ids = [self.root_id] if not isinstance(self.root_id, list) else self.root_id
cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids] cis = [CI.get_by_id(_id) or abort(404, ErrFormat.ci_not_found.format("id={}".format(_id))) for _id in ids]
@@ -197,7 +197,8 @@ class Search(object):
sort=self.sort, sort=self.sort,
ci_ids=merge_ids, ci_ids=merge_ids,
parent_node_perm_passed=parent_node_perm_passed, parent_node_perm_passed=parent_node_perm_passed,
use_ci_filter=use_ci_filter).search() use_ci_filter=use_ci_filter,
only_ids=only_ids).search()
def _get_ci_filter(self, filter_perms, ci_filters=None): def _get_ci_filter(self, filter_perms, ci_filters=None):
ci_filters = ci_filters or [] ci_filters = ci_filters or []

View File

@@ -274,7 +274,8 @@ class AttributeValueManager(object):
if attr.is_list: if attr.is_list:
existed_attrs = value_table.get_by(attr_id=attr.id, ci_id=ci.id, to_dict=False) existed_attrs = value_table.get_by(attr_id=attr.id, ci_id=ci.id, to_dict=False)
existed_values = [i.value for i in existed_attrs] existed_values = [(ValueTypeMap.serialize[attr.value_type](i.value) if
i.value or i.value == 0 else i.value) for i in existed_attrs]
# Comparison array starts from which position changes # Comparison array starts from which position changes
min_len = min(len(value), len(existed_values)) min_len = min(len(value), len(existed_values))
@@ -283,17 +284,15 @@ class AttributeValueManager(object):
if value[index] != existed_values[index]: if value[index] != existed_values[index]:
break break
index += 1 index += 1
added = value[index:]
deleted = existed_values[index:]
# Delete first and then add to ensure id sorting # Delete first and then add to ensure id sorting
for v in deleted: for idx in range(index, len(existed_attrs)):
existed_attr = existed_attrs[existed_values.index(v)] existed_attr = existed_attrs[idx]
existed_attr.delete(flush=False, commit=False) existed_attr.delete(flush=False, commit=False)
changed.append((ci.id, attr.id, OperateType.DELETE, v, None, ci.type_id)) changed.append((ci.id, attr.id, OperateType.DELETE, existed_values[idx], None, ci.type_id))
for v in added: for idx in range(index, len(value)):
value_table.create(ci_id=ci.id, attr_id=attr.id, value=v, flush=False, commit=False) value_table.create(ci_id=ci.id, attr_id=attr.id, value=value[idx], flush=False, commit=False)
changed.append((ci.id, attr.id, OperateType.ADD, None, v, ci.type_id)) changed.append((ci.id, attr.id, OperateType.ADD, None, value[idx], ci.type_id))
else: else:
existed_attr = value_table.get_by(attr_id=attr.id, ci_id=ci.id, first=True, to_dict=False) existed_attr = value_table.get_by(attr_id=attr.id, ci_id=ci.id, first=True, to_dict=False)
existed_value = existed_attr and existed_attr.value existed_value = existed_attr and existed_attr.value

View File

@@ -153,11 +153,11 @@ class ACLManager(object):
if resource: if resource:
return ResourceCRUD.delete(resource.id, rebuild=rebuild) return ResourceCRUD.delete(resource.id, rebuild=rebuild)
def has_permission(self, resource_name, resource_type, perm, resource_id=None): def has_permission(self, resource_name, resource_type, perm, resource_id=None, rid=None):
if is_app_admin(self.app_id): if is_app_admin(self.app_id):
return True return True
role = self._get_role(current_user.username) role = self._get_role(current_user.username) if rid is None else RoleCache.get(rid)
role or abort(404, ErrFormat.role_not_found.format(current_user.username)) role or abort(404, ErrFormat.role_not_found.format(current_user.username))

View File

@@ -3,6 +3,7 @@
import msgpack import msgpack
import redis_lock import redis_lock
from flask import current_app
from api.extensions import cache from api.extensions import cache
from api.extensions import rd from api.extensions import rd
@@ -157,9 +158,9 @@ class RoleRelationCache(object):
PREFIX_RESOURCES2 = "RoleRelationResources2::id::{0}::AppId::{1}" PREFIX_RESOURCES2 = "RoleRelationResources2::id::{0}::AppId::{1}"
@classmethod @classmethod
def get_parent_ids(cls, rid, app_id): def get_parent_ids(cls, rid, app_id, force=False):
parent_ids = cache.get(cls.PREFIX_PARENT.format(rid, app_id)) parent_ids = cache.get(cls.PREFIX_PARENT.format(rid, app_id))
if not parent_ids: if not parent_ids or force:
from api.lib.perm.acl.role import RoleRelationCRUD from api.lib.perm.acl.role import RoleRelationCRUD
parent_ids = RoleRelationCRUD.get_parent_ids(rid, app_id) parent_ids = RoleRelationCRUD.get_parent_ids(rid, app_id)
cache.set(cls.PREFIX_PARENT.format(rid, app_id), parent_ids, timeout=0) cache.set(cls.PREFIX_PARENT.format(rid, app_id), parent_ids, timeout=0)
@@ -167,9 +168,9 @@ class RoleRelationCache(object):
return parent_ids return parent_ids
@classmethod @classmethod
def get_child_ids(cls, rid, app_id): def get_child_ids(cls, rid, app_id, force=False):
child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid, app_id)) child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid, app_id))
if not child_ids: if not child_ids or force:
from api.lib.perm.acl.role import RoleRelationCRUD from api.lib.perm.acl.role import RoleRelationCRUD
child_ids = RoleRelationCRUD.get_child_ids(rid, app_id) child_ids = RoleRelationCRUD.get_child_ids(rid, app_id)
cache.set(cls.PREFIX_CHILDREN.format(rid, app_id), child_ids, timeout=0) cache.set(cls.PREFIX_CHILDREN.format(rid, app_id), child_ids, timeout=0)
@@ -177,14 +178,15 @@ class RoleRelationCache(object):
return child_ids return child_ids
@classmethod @classmethod
def get_resources(cls, rid, app_id): def get_resources(cls, rid, app_id, force=False):
""" """
:param rid: :param rid:
:param app_id: :param app_id:
:param force:
:return: {id2perms: {resource_id: [perm,]}, group2perms: {group_id: [perm, ]}} :return: {id2perms: {resource_id: [perm,]}, group2perms: {group_id: [perm, ]}}
""" """
resources = cache.get(cls.PREFIX_RESOURCES.format(rid, app_id)) resources = cache.get(cls.PREFIX_RESOURCES.format(rid, app_id))
if not resources: if not resources or force:
from api.lib.perm.acl.role import RoleCRUD from api.lib.perm.acl.role import RoleCRUD
resources = RoleCRUD.get_resources(rid, app_id) resources = RoleCRUD.get_resources(rid, app_id)
if resources['id2perms'] or resources['group2perms']: if resources['id2perms'] or resources['group2perms']:
@@ -193,9 +195,9 @@ class RoleRelationCache(object):
return resources or {} return resources or {}
@classmethod @classmethod
def get_resources2(cls, rid, app_id): def get_resources2(cls, rid, app_id, force=False):
r_g = cache.get(cls.PREFIX_RESOURCES2.format(rid, app_id)) r_g = cache.get(cls.PREFIX_RESOURCES2.format(rid, app_id))
if not r_g: if not r_g or force:
res = cls.get_resources(rid, app_id) res = cls.get_resources(rid, app_id)
id2perms = res['id2perms'] id2perms = res['id2perms']
group2perms = res['group2perms'] group2perms = res['group2perms']
@@ -232,20 +234,20 @@ class RoleRelationCache(object):
for _app_id in app_ids: for _app_id in app_ids:
cls.clean(rid, _app_id) cls.clean(rid, _app_id)
cls.get_parent_ids(rid, _app_id) cls.get_parent_ids(rid, _app_id, force=True)
cls.get_child_ids(rid, _app_id) cls.get_child_ids(rid, _app_id, force=True)
resources = cls.get_resources(rid, _app_id) resources = cls.get_resources(rid, _app_id, force=True)
if resources.get('id2perms') or resources.get('group2perms'): if resources.get('id2perms') or resources.get('group2perms'):
HasResourceRoleCache.add(rid, _app_id) HasResourceRoleCache.add(rid, _app_id)
else: else:
HasResourceRoleCache.remove(rid, _app_id) HasResourceRoleCache.remove(rid, _app_id)
cls.get_resources2(rid, _app_id) cls.get_resources2(rid, _app_id, force=True)
@classmethod @classmethod
@flush_db @flush_db
def rebuild2(cls, rid, app_id): def rebuild2(cls, rid, app_id):
cache.delete(cls.PREFIX_RESOURCES2.format(rid, app_id)) cache.delete(cls.PREFIX_RESOURCES2.format(rid, app_id))
cls.get_resources2(rid, app_id) cls.get_resources2(rid, app_id, force=True)
@classmethod @classmethod
def clean(cls, rid, app_id): def clean(cls, rid, app_id):

View File

@@ -379,16 +379,16 @@ class RoleCRUD(object):
resource_type_id = resource_type and resource_type.id resource_type_id = resource_type and resource_type.id
result = dict(resources=dict(), groups=dict()) result = dict(resources=dict(), groups=dict())
s = time.time() # s = time.time()
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid, app_id) parent_ids = RoleRelationCRUD.recursive_parent_ids(rid, app_id)
current_app.logger.info('parent ids {0}: {1}'.format(parent_ids, time.time() - s)) # current_app.logger.info('parent ids {0}: {1}'.format(parent_ids, time.time() - s))
for parent_id in parent_ids: for parent_id in parent_ids:
_resources, _groups = cls._extend_resources(parent_id, resource_type_id, app_id) _resources, _groups = cls._extend_resources(parent_id, resource_type_id, app_id)
current_app.logger.info('middle1: {0}'.format(time.time() - s)) # current_app.logger.info('middle1: {0}'.format(time.time() - s))
_merge(result['resources'], _resources) _merge(result['resources'], _resources)
current_app.logger.info('middle2: {0}'.format(time.time() - s)) # current_app.logger.info('middle2: {0}'.format(time.time() - s))
current_app.logger.info(len(_groups)) # current_app.logger.info(len(_groups))
if not group_flat: if not group_flat:
_merge(result['groups'], _groups) _merge(result['groups'], _groups)
else: else:
@@ -399,7 +399,7 @@ class RoleCRUD(object):
item.setdefault('permissions', []) item.setdefault('permissions', [])
item['permissions'] = list(set(item['permissions'] + _groups[rg_id]['permissions'])) item['permissions'] = list(set(item['permissions'] + _groups[rg_id]['permissions']))
result['resources'][item['id']] = item result['resources'][item['id']] = item
current_app.logger.info('End: {0}'.format(time.time() - s)) # current_app.logger.info('End: {0}'.format(time.time() - s))
result['resources'] = list(result['resources'].values()) result['resources'] = list(result['resources'].values())
result['groups'] = list(result['groups'].values()) result['groups'] = list(result['groups'].values())

View File

@@ -49,6 +49,8 @@
import _ from 'lodash' import _ from 'lodash'
import '@wangeditor/editor/dist/css/style.css' import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { i18nChangeLanguage } from '@wangeditor/editor'
export default { export default {
name: 'NoticeContent', name: 'NoticeContent',
components: { Editor, Toolbar }, components: { Editor, Toolbar },
@@ -76,6 +78,10 @@ export default {
if (editor == null) return if (editor == null) return
editor.destroy() // When the component is destroyed, destroy the editor in time editor.destroy() // When the component is destroyed, destroy the editor in time
}, },
beforeCreate() {
const locale = this.$i18n.locale === 'zh' ? 'zh-CN' : 'en'
i18nChangeLanguage(locale)
},
methods: { methods: {
onCreated(editor) { onCreated(editor) {
this.editor = Object.seal(editor) // Be sure to use Object.seal(), otherwise an error will be reported this.editor = Object.seal(editor) // Be sure to use Object.seal(), otherwise an error will be reported

View File

@@ -198,6 +198,9 @@ export default {
if (this.type === 'resourceSearch') { if (this.type === 'resourceSearch') {
this.getCITypeGroups() this.getCITypeGroups()
} }
if (this.typeId) {
this.currenCiType = [this.typeId]
}
}, },
methods: { methods: {
// toggleAdvanced() { // toggleAdvanced() {

View File

@@ -4,6 +4,7 @@ const cmdb_en = {
configTable: 'Config Table', configTable: 'Config Table',
menu: { menu: {
views: 'Views', views: 'Views',
resources: 'Resources',
config: 'Configuration', config: 'Configuration',
backend: 'Management', backend: 'Management',
ciTable: 'Resource Views', ciTable: 'Resource Views',
@@ -69,9 +70,9 @@ const cmdb_en = {
adAutoInLib: 'Save as CI auto', adAutoInLib: 'Save as CI auto',
adInterval: 'Collection frequency', adInterval: 'Collection frequency',
byInterval: 'by interval', byInterval: 'by interval',
allNodes: 'All nodes', allNodes: 'All Instances',
specifyNodes: 'Specify Node', specifyNodes: 'Specify Instance',
specifyNodesTips: 'Please fill in the specify node!', specifyNodesTips: 'Please fill in the specify instance!',
username: 'Username', username: 'Username',
password: 'Password', password: 'Password',
link: 'Link', link: 'Link',
@@ -144,7 +145,7 @@ const cmdb_en = {
triggerDataChange: 'Data changes', triggerDataChange: 'Data changes',
triggerDate: 'Date attribute', triggerDate: 'Date attribute',
triggerEnable: 'Turn on', triggerEnable: 'Turn on',
descInput: 'Please enter remarks', descInput: 'Please enter descriptions',
triggerCondition: 'Triggering conditions', triggerCondition: 'Triggering conditions',
addInstance: 'Add new instance', addInstance: 'Add new instance',
deleteInstance: 'Delete instance', deleteInstance: 'Delete instance',

View File

@@ -4,8 +4,9 @@ const cmdb_zh = {
configTable: '配置表格', configTable: '配置表格',
menu: { menu: {
views: '视图', views: '视图',
resources: '资源',
config: '配置', config: '配置',
backend: '管理', backend: '管理',
ciTable: '资源数据', ciTable: '资源数据',
ciTree: '资源层级', ciTree: '资源层级',
ciSearch: '资源搜索', ciSearch: '资源搜索',
@@ -69,9 +70,9 @@ const cmdb_zh = {
adAutoInLib: '自动入库', adAutoInLib: '自动入库',
adInterval: '采集频率', adInterval: '采集频率',
byInterval: '按间隔', byInterval: '按间隔',
allNodes: '所有节点', allNodes: '所有实例',
specifyNodes: '指定节点', specifyNodes: '指定实例',
specifyNodesTips: '请填写指定节点', specifyNodesTips: '请填写指定实例',
username: '用户名', username: '用户名',
password: '密码', password: '密码',
link: '链接', link: '链接',
@@ -144,7 +145,7 @@ const cmdb_zh = {
triggerDataChange: '数据变更', triggerDataChange: '数据变更',
triggerDate: '日期属性', triggerDate: '日期属性',
triggerEnable: '开启', triggerEnable: '开启',
descInput: '请输入备注', descInput: '请输入描述',
triggerCondition: '触发条件', triggerCondition: '触发条件',
addInstance: '新增实例', addInstance: '新增实例',
deleteInstance: '删除实例', deleteInstance: '删除实例',

View File

@@ -19,7 +19,7 @@ const genCmdbRoutes = async () => {
{ {
path: '/cmdb/disabled1', path: '/cmdb/disabled1',
name: 'cmdb_disabled1', name: 'cmdb_disabled1',
meta: { title: 'cmdb.menu.views', disabled: true }, meta: { title: 'cmdb.menu.resources', disabled: true },
}, },
{ {
path: '/cmdb/resourceviews', path: '/cmdb/resourceviews',

View File

@@ -107,6 +107,7 @@
v-decorator="[list.name, { rules: [{ required: false }] }]" v-decorator="[list.name, { rules: [{ required: false }] }]"
style="width: 100%" style="width: 100%"
:format="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'" :format="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
:valueFormat="getFieldType(list.name) == '4' ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm:ss'"
v-if="getFieldType(list.name) === '4' || getFieldType(list.name) === '3'" v-if="getFieldType(list.name) === '4' || getFieldType(list.name) === '3'"
:showTime="getFieldType(list.name) === '4' ? false : { format: 'HH:mm:ss' }" :showTime="getFieldType(list.name) === '4' ? false : { format: 'HH:mm:ss' }"
/> />
@@ -370,7 +371,7 @@ export default {
return 'select%%multiple' return 'select%%multiple'
} }
return 'select' return 'select'
} else if (_find.value_type === '0' || _find.value_type === '1') { } else if ((_find.value_type === '0' || _find.value_type === '1') && !_find.is_list) {
return 'input_number' return 'input_number'
} else if (_find.value_type === '4' || _find.value_type === '3') { } else if (_find.value_type === '4' || _find.value_type === '3') {
return _find.value_type return _find.value_type

View File

@@ -182,7 +182,7 @@ export default {
const edges = [] const edges = []
this.parentCITypes.forEach((parent) => { this.parentCITypes.forEach((parent) => {
const _findCiType = ci_types_list.find((item) => item.id === parent.id) const _findCiType = ci_types_list.find((item) => item.id === parent.id)
if (this.firstCIs[parent.name]) { if (this.firstCIs[parent.name] && _findCiType) {
const unique_id = _findCiType.show_id || _findCiType.unique_id const unique_id = _findCiType.show_id || _findCiType.unique_id
const _findUnique = parent.attributes.find((attr) => attr.id === unique_id) const _findUnique = parent.attributes.find((attr) => attr.id === unique_id)
const unique_name = _findUnique?.name const unique_name = _findUnique?.name
@@ -225,7 +225,7 @@ export default {
}) })
this.childCITypes.forEach((child) => { this.childCITypes.forEach((child) => {
const _findCiType = ci_types_list.find((item) => item.id === child.id) const _findCiType = ci_types_list.find((item) => item.id === child.id)
if (this.secondCIs[child.name]) { if (this.secondCIs[child.name] && _findCiType) {
const unique_id = _findCiType.show_id || _findCiType.unique_id const unique_id = _findCiType.show_id || _findCiType.unique_id
const _findUnique = child.attributes.find((attr) => attr.id === unique_id) const _findUnique = child.attributes.find((attr) => attr.id === unique_id)
const unique_name = _findUnique?.name const unique_name = _findUnique?.name

View File

@@ -112,7 +112,7 @@
<a-tab-pane key="tab_5"> <a-tab-pane key="tab_5">
<span slot="tab"><ops-icon type="itsm-association" />{{ $t('cmdb.ci.relITSM') }}</span> <span slot="tab"><ops-icon type="itsm-association" />{{ $t('cmdb.ci.relITSM') }}</span>
<div :style="{ padding: '24px', height: '100%' }"> <div :style="{ padding: '24px', height: '100%' }">
<related-itsm-table :ci_id="ci._id" :ciHistory="ciHistory" :itsmInstalled="itsmInstalled" /> <RelatedItsmTable ref="relatedITSMTable" :ci_id="ci._id" :ciHistory="ciHistory" :itsmInstalled="itsmInstalled" :attrList="attrList" />
</div> </div>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
@@ -140,8 +140,6 @@ import CiDetailRelation from './ciDetailRelation.vue'
import TriggerTable from '../../operation_history/modules/triggerTable.vue' import TriggerTable from '../../operation_history/modules/triggerTable.vue'
import RelatedItsmTable from './ciDetailRelatedItsmTable.vue' import RelatedItsmTable from './ciDetailRelatedItsmTable.vue'
import CiRollbackForm from './ciRollbackForm.vue' import CiRollbackForm from './ciRollbackForm.vue'
import { sleep } from '@/utils/util'
export default { export default {
name: 'CiDetailTab', name: 'CiDetailTab',
components: { components: {

View File

@@ -20,7 +20,8 @@
</div> </div>
<SearchForm <SearchForm
ref="search" ref="search"
type="resourceSearch" :type="type"
:typeId="typeId"
@refresh="handleSearch" @refresh="handleSearch"
:preferenceAttrList="allAttributesList" :preferenceAttrList="allAttributesList"
@updateAllAttributesList="updateAllAttributesList" @updateAllAttributesList="updateAllAttributesList"
@@ -205,7 +206,7 @@ import _ from 'lodash'
import SearchForm from '../../components/searchForm/SearchForm.vue' import SearchForm from '../../components/searchForm/SearchForm.vue'
import { searchCI } from '../../api/ci' import { searchCI } from '../../api/ci'
import { searchAttributes, getCITypeAttributesByTypeIds, getCITypeAttributesById } from '../../api/CITypeAttr' import { searchAttributes, getCITypeAttributesByTypeIds, getCITypeAttributesById } from '../../api/CITypeAttr'
import { getCITypes } from '../../api/CIType' import { getCITypes, getCIType } from '../../api/CIType'
import { getSubscribeAttributes } from '../../api/preference' import { getSubscribeAttributes } from '../../api/preference'
import { getCITableColumns } from '../../utils/helper' import { getCITableColumns } from '../../utils/helper'
import EditAttrsPopover from '../ci/modules/editAttrsPopover.vue' import EditAttrsPopover from '../ci/modules/editAttrsPopover.vue'
@@ -221,6 +222,14 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
typeId: {
type: Number,
default: null
},
type: {
type: String,
default: 'resourceSearch'
}
}, },
data() { data() {
return { return {
@@ -250,8 +259,14 @@ export default {
} }
}, },
mounted() { mounted() {
this.getAllAttr() if (this.typeId) {
this.getAllCiTypes() this.getCIType(this.typeId)
this.getAttrsByType(this.typeId)
this.loadInstance()
} else {
this.getAllAttr()
this.getAllCiTypes()
}
}, },
methods: { methods: {
getAllCiTypes() { getAllCiTypes() {
@@ -259,6 +274,17 @@ export default {
this.ciTypes = res.ci_types this.ciTypes = res.ci_types
}) })
}, },
getCIType(typeId) {
getCIType(typeId).then((res) => {
this.ciTypes = res.ci_types
})
},
async getAttrsByType(typeId) {
await getCITypeAttributesById(typeId).then((res) => {
this.allAttributesList = res.attributes
this.originAllAttributesList = res.attributes
})
},
async getAllAttr() { async getAllAttr() {
await searchAttributes({ page_size: 9999 }).then((res) => { await searchAttributes({ page_size: 9999 }).then((res) => {
this.allAttributesList = res.attributes this.allAttributesList = res.attributes